Je souhaite tester la méthode save () de la session d'hibernation à l'aide de la structure de test Spring. La méthode @Test est:
@Test
@Transactional
public void testSave() {
User expected = createUser();
getGenericDao().currentSession().save(expected);
User actual = getUser(generatedId);
assertUsersEqual(expected,actual);
}
Je veux jeter l'utilisateur dans la base de données. Je veux que mon utilisateur soit dans la base de données après cette méthode
getGenericDao().currentSession().save(expected);
Ensuite, je souhaite accéder à la base de données à l'aide du framework de données Spring et récupérer cet utilisateur enregistré à la ligne suivante:
User actual = getUser(generatedId);
J'ai essayé d'utiliser la méthode de vidage en veille prolongée comme:
currentSession().setFlushMode(MANUAL);
//do saving here
currentSession().flush();
Il ne jette pas mon utilisateur dans la base de données! Cependant, si je n'utilise pas l'annotation de ressort @Transactional et ne sauvegarde pas mon utilisateur dans une transaction de ressort programmatique, j'obtiens ce que je veux. Malheureusement, l’utilisateur enregistré dans la base de données n’est pas restauré car il n’existe pas de ressort @Transactional. Par conséquent, ma méthode de test modifie la base de données et le comportement des méthodes de test suivantes.
Je dois donc vider mon utilisateur dans la méthode de test interne de la base de données (pas à la fin) et à la fin de la méthode de test annuler toutes les modifications apportées à la base de données.
UPDATEsuggestion de préparer la méthode comme suit:
@Transactional
public void doSave(User user){
getGenericDao().currentSession().save(user);
}
Et appeler doSave dans testSave ne fait rien. Je n'ai toujours pas d'utilisateur dans la base de données après avoir exécuté cette méthode. Je place le point d'arrêt et vérifie ma base de données en ligne de commande.
UPDATEMerci beaucoup pour votre réponse. Le problème est que la méthode flush () ne met pas mon utilisateur dans la base de données. J'ai essayé Isolation.READ_UNCOMMITTED et ne met pas mon utilisateur dans la base de données. Je peux réaliser ce que je veux, mais seulement si je désactive la transaction de printemps sur la méthode @Test et que je sauvegarde les transactions programmatiques. MAIS alors la méthode @Test n’est pas restaurée, laissant l’utilisateur enregistré pour les méthodes @Test suivantes. Ici, la méthode @Test pour enregistrer l'utilisateur n'est pas aussi dangereuse que la méthode @Test pour supprimer l'utilisateur, car elle n'est pas restaurée. Donc, il doit y avoir un support transactionnel pour la méthode @Test avec laquelle je ne peux de toute façon pas mettre mon utilisateur (ou supprimer) dans la base de données. En réalité, l'utilisateur est placé (ou supprimé) dans la base de données uniquement après la fin de la méthode @Test et après la transaction pour la méthode @Test. Je souhaite donc enregistrer mon utilisateur dans la base de données au milieu de la méthode @Test et le restaurer à la fin de la méthode @Test
Je vous remercie!
Enfin, je suis resté sur la solution suivante:
Premièrement, mes méthodes @Test
ne s'exécutent pas dans le support print @Transactional
. Voir cet article pour savoir à quel point il peut être dangereux. Ensuite, au lieu d’utiliser les beans @Repository
dans les méthodes @Test
, j’ai autowire des beans @Service
qui utilisent l’annotation @Transactional
. Le miracle est cette méthode @Test
comme celle-ci
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testSave() {
Answer created = createAnswer();
Long generatedId = answerService.save(created);
//at this moment answer is already in db
Answer actual=getAnswerById(generatedId);
... }
place mon objet Answer dans la base de données (juste après answerService.save(created);
) et la méthode getAnswerById
va dans la base de données et l'extrait pour vérifier si la sauvegarde était correcte.
Pour éliminer les modifications apportées à la base de données dans la méthode @Test
, je recrée la base de données par JdbcTestUtils.executeSqlScript
.
@Transactional
( Les pièges printaniers: les tests transactionnels considérés comme dangereux ) J'ai utilisé @org.springframework.test.context.jdbc.Sql
pour re-peupler la base de données dans mes tests de service et @Transactional
pour les contrôleurs.ConstraintViolationException
pour le test de mise à jour du contrôleur avec des données non valides ont été levées uniquement lorsque la transaction est validée. J'ai donc trouvé 3 options: @Commit
ou avec @Transactional(propagation = Propagation.NEVER)
. Soyez conscient du changement de base de données.TestTransaction
Code:
TestTransaction.flagForCommit();
TestTransaction.end();
TransactionTemplate
Code:
@Autowired
private PlatformTransactionManager platformTransactionManager;
@Test(expected = Exception.class)
public void testUpdate() throws Exception {
TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
String json = ...
transactionTemplate.execute(ts -> {
try {
mockMvc.perform(put(REST_URL + USER_ID)
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(status().isOk());
...
} catch (Exception e) {
e.printStackTrace();
}
return null;
});
Si flush
ne fonctionne pas, tout dépend du niveau d'isolation de votre base de données.
Isolation
est l'une des propriétés ACID
de la base de données, qui définit comment/quand les modifications apportées par une opération deviennent visibles par d'autres opérations simultanées.
Je crois que votre niveau d'isolement est défini sur Read Committed
ou Repeatable Read
.
Vous devez également vous occuper du paquet importé: Dans mon cas, j'ai importé
import javax.transaction.Transactional;
au lieu de
import org.springframework.transaction.annotation.Transactional;