10

Ich möchte die save() -Methode der Hibernation-Sitzung mithilfe des Spring-Test-Frameworks testen. @Test Methode ist:Wie spült man Daten in db innerhalb einer aktiven Federtransaktion?

@Test 
@Transactional 
public void testSave() { 
User expected = createUser(); 
getGenericDao().currentSession().save(expected); 
User actual = getUser(generatedId); 
assertUsersEqual(expected,actual); 
} 

ich Benutzer in der Datenbank spülen will. Ich möchte meinen Benutzer

getGenericDao().currentSession().save(expected); 

Dann Datenbank gehen Ich will nach dieser Methode in der Datenbank sein Federdaten-Framework und holen diese Benutzer durch nächste Zeile gespeichert:

User actual = getUser(generatedId); 

Ich habe versucht, Hibernate zu verwenden Spülmethode wie:

currentSession().setFlushMode(MANUAL); 
//do saving here 
currentSession().flush(); 

Es spült meinen Benutzer nicht in die Datenbank! Wenn ich jedoch nicht @Transactional Spring Annotation verwenden und meinen Benutzer in programmatischen Feder-Transaktion speichern, erreiche ich, was ich will. Leider wird der Benutzer, der in db gespeichert wurde, nicht zurückgesetzt, da es keine Feder @Transactional gibt. Daher ändert meine Testmethode die db und das Verhalten der nachfolgenden Testmethoden.

Also muss ich meinen Benutzer in db innerhalb der Testmethode (nicht am Ende) und am Ende der Testmethode Rollback alle Änderungen an db.

UPDATE Vorschlag Verfahren zur Herstellung wie folgt:

@Transactional 
public void doSave(User user){ 
    getGenericDao().currentSession().save(user); 
} 

Und rufen DoSave innen testSave nichts tut. Noch habe ich keinen Benutzer in db nach dem Ausführen dieser Methode. Ich setze Haltepunkt und überprüfe meine DB von der Befehlszeile.

UPDATE Vielen Dank für die Antwort. Das Problem ist, dass die Methode flush() meinen Benutzer nicht in die Datenbank versetzt. Ich habe Isolation.READ_UNCOMMITTED versucht, und mein Benutzer wird nicht in die Datenbank gestellt. Ich kann erreichen, was ich will, aber nur, wenn ich die Feder-Transaktion auf @Test-Methode abstelle und in programmatischer Transaktion speichere. ABER dann wird @Test-Methode nicht zurückgerollt und der gespeicherte Benutzer für nachfolgende @Test-Methoden zurückgelassen. Hier @Test-Methode zum Speichern von Benutzer ist nicht so gefährlich wie @Test-Methode, um Benutzer zu löschen, da es nicht zurückgesetzt wird. Also muss es transaktionale Unterstützung für die @Test-Methode geben, mit der ich meinen Benutzer sowieso nicht in db setzen (oder löschen) kann. Tatsächlich wird der Benutzer nur dann in db versetzt (oder gelöscht), wenn die @Test-Methode beendet und die Transaktion für die @Test-Methode aktiviert ist. So möchte ich meinen Benutzer in db in der Mitte von @Test-Methode speichern und es am Ende von @Test Methode

Vielen Dank!

+0

Können Sie uns auch Ihre GenericDao-Klasse geben? EDIT: eigentlich denke ich, ich sehe das Problem. Die aktuelle Transaktion muss festgeschrieben werden, bevor die Daten tatsächlich in der Datenbank gespeichert werden. Sie benötigen also zwei @Transactional-Methoden: eine zum Speichern und eine weitere zum Laden. –

+0

Bitte schauen Sie, ob ich es richtig gemacht habe –

+0

Schließen. Ich meine zwei Transaktionsfunktionen, die von einer nicht-transaktionalen Funktion aufgerufen werden. Das sollte funktionieren ... –

Antwort

1

Wenn flush nicht funktioniert, hängt es sehr von Ihrer Datenbankisolationsstufe ab.

Isolation ist eine der ACID Eigenschaften der Datenbank, die definiert, wie/wann die von einer Operation vorgenommenen Änderungen für andere gleichzeitige Operationen sichtbar werden.

Ich glaube, Ihre Isolationsstufe ist auf Read Committed oder Repeatable Read eingestellt.

8

Schließlich steckte ich zu folgenden Lösung:

Zuerst meine @Test Methoden laufen nicht im Frühjahr @Transactional Unterstützung. Siehe this article, um zu wissen, wie gefährlich es sein kann. Als nächstes statt @Repository Bohnen innerhalb @Test Methoden autowire I @Service Bohnen, die @Transactional Anmerkung verwenden. Das Wunder ist, dass @Test Verfahren wie diese

@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); 
... } 

Objekt in der Datenbank meiner Antwort setzen (nur nach answerService.save(created);) und Verfahren getAnswerById geht an DB und extrahieren es zu überprüfen, ob speichert richtig war.
Um Änderungen an Datenbank in @Test Verfahren zu eliminieren ich Datenbank von JdbcTestUtils.executeSqlScript

+1

Anstatt JdbcTestUtils zu verwenden, können Sie @DirtiesContext-Annotationen anwenden. –

0

Sie sollten auch kümmern sich um die importedpackage neu: in meinem Fall habe ich

importiert

Import javax.transaction.Transactional;

anstelle von

import org.springframework.transaction.annotation.Transactional;

1
  1. Werfen Sie einen Blick here mit über @Transactional Tests Warnung (Spring Pitfalls: Transactional tests considered harmful). Ich habe @org.springframework.test.context.jdbc.Sql verwendet, um DB in meinen Service-Tests und @Transactional für Controller wieder zu füllen.
  2. ConstraintViolationException für Controller-Update-Test mit ungültigen Daten wurden nur ausgelöst, wenn Transaktion festgeschrieben wird. Also habe ich 3 Optionen gefunden:
    • 2.1 Anmerkungs-Test mit @Commit oder mit @Transactional(propagation = Propagation.NEVER). Beachten Sie die DB-Änderung.
    • 2.2 Verwendung TestTransaction

Code:

 TestTransaction.flagForCommit(); 
    TestTransaction.end(); 
  • 2,3 Verwenden 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; 
     }); 
Verwandte Themen