2016-05-24 5 views
2

Ich verwende Spring Boot 1.4.0.M3.Die erwartete Ausnahme kann nicht getestet werden, wenn @Transactional mit @Commit verwendet wird

Ich habe ein @Entity, dass ein username hat, die eindeutig sein sollte:

@NotNull 
@Column(unique = true) 
@Pattern(regexp = "[a-zA-Z_\\-\\.0-9]+") 
@Size(min = 1, max = 30) 
private String username; 

Mit einem Spring Data Repository, ich will es testen, ob eine Ausnahme sein, wenn ein doppelter Benutzername verwendet wird. Dieser Test funktioniert:

@SpringBootTest 
@RunWith(SpringRunner.class) 
public class UserRepositoryIntegrationTest { 

    @Autowired 
    private UserRepository repository; 

    @Test(expected = DataIntegrityViolationException.class) 
    public void test() { 
     repository.save(User.createAdmin(repository.nextId(), "wim", "123456")); 
     repository.save(User.createAdmin(repository.nextId(), "wim", "123456")); 
    } 
} 

Wenn jedoch @Transactional mit @Commit Hinzufügen dieser Test fehlschlägt:

@SpringBootTest 
@RunWith(SpringRunner.class) 
@Transactional 
public class UserRepositoryIntegrationTest { 

    @Autowired 
    private UserRepository repository; 

    @Test(expected = DataIntegrityViolationException.class) 
    @Commit 
    public void test() { 
     repository.save(User.createAdmin(repository.nextId(), "wim", "123456")); 
     repository.save(User.createAdmin(repository.nextId(), "wim", "123456")); 
    } 
} 

Aber ein Blick auf die Protokollierung wird die DataIntegrityViolationException geworfen:

2016-05-24 09:05:16.619 ERROR 22790 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Unique index or primary key violation: "UK_D8HGQ87BS4VPMC81NQ9G69G8X_INDEX_D ON PUBLIC.MYPROJECT_USER(USERNAME) VALUES ('wim', 1)"; SQL statement: insert into myproject_user (password, username, role, id) values (?, ?, 'ADMIN', ?) [23505-191] 2016-05-24 09:05:16.620 INFO 22790 --- [
main] o.h.e.j.b.internal.AbstractBatchImpl : HHH000010: On release of batch it still contained JDBC statements 2016-05-24 09:05:16.629 WARN 22790 --- [ main] o.s.test.context.TestContextManager
: Caught exception while allowing TestExecutionListener [org.springframew[email protected]589b3632] to process 'after' execution for test: method [public void com.spring.boot.test.user.UserRepositoryIntegrationTest.test()], instance [[email protected]], exception [java.lang.AssertionError: Expected exception: org.springframework.dao.DataIntegrityViolationException]

Warum Scheitert der Test? Könnte es sein, dass JUnit überprüft, ob die Ausnahme ausgelöst wird, bevor Spring die Transaktion festschreibt?

+1

die als erwartet ist ... Die Ausnahme tritt nach der Transaktion zu begehen.Die Transaktion wird NACH dem Beenden der Methode ausgeführt. SO gibt es im Moment der Überprüfung keine Ausnahme. Sie haben damit das Verhalten des Tests geändert. Sie müssen innerhalb Ihrer Methode festlegen. –

+1

"Ausnahme abgefangen, während TestExecutionListener erlaubt wurde", sagte alles. Sie möchten einen Commit ausführen, aber Sie möchten bestätigen, dass das Commit fehlschlägt. JUnit und Spring sind sich ihrer nicht bewusst, so dass Sie keine JUnit-Assertion für dieses Zeug verwenden können. Wie Martin sagte, benutze 'TestTransaction # end' in deinem Test. –

+0

Danke für das Zeigen auf diese Klasse. Wusste nicht, dass es existiert. Ich habe eine Antwort mit der vollständigen Arbeitsversion hinzugefügt. –

Antwort

2

Basierend auf den Kommentaren von M.Deinum und Stephane Nicoll, ist dies die Arbeitsversion:

@SpringBootTest 
@RunWith(SpringRunner.class) 
@Transactional 
public class UserRepositoryIntegrationTest { 

    @Autowired 
    private UserRepository repository; 

    @Test(expected = DataIntegrityViolationException.class) 
    public void test() { 
     repository.save(User.createAdmin(repository.nextId(), "wim", "123456")); 
     repository.save(User.createAdmin(repository.nextId(), "wim", "123456")); 

     TestTransaction.flagForCommit(); 
     TestTransaction.end(); 
    } 
} 

Beachten Sie, dass beide statische Methoden aufgerufen werden müssen, damit es funktioniert.

+0

Großartig Sie haben meinen Tag gemacht, das ist die perfekte Antwort, kurz und gut –

2

Sie müssen nicht festlegen. Sie müssen nur Ihre Arbeitseinheit in die Datenbank spülen, um die DataIntegrityViolationException zu sehen.

Dies wird in der Spring Reference Manual beschrieben. Scrollen Sie nach unten zum Hinweis auf "falsch positive". Beachten Sie jedoch, dass die EntityManager über @PersistenceContext (nicht @Autowired) injiziert werden muss.

@RunWith(SpringRunner.class) 
@SpringBootTest 
@Transactional 
public class UserRepositoryIntegrationTest { 

    @Autowired 
    UserRepository repository; 

    @PersistenceContext 
    EntityManager entityManager; 

    @Test(expected = DataIntegrityViolationException.class) 
    public void test() { 
     repository.save(User.createAdmin(repository.nextId(), "wim", "123456")); 
     repository.save(User.createAdmin(repository.nextId(), "wim", "123456")); 

     entityManager.flush(); 
    } 
} 

Grüße,

Sam (Autor des Frühlings Testcontext Rahmen)

1

Ich brauche zu testen DataIntegrityViolationException in MVC-Controller-Test (:

Folgendes sollte für Sie funktionieren @WebAppConfiguration) und tun @ResponseStatus Änderung in der Anwendung von @ControllerAdvice .So Ihre Ansatz passt nicht für mich, es spült zu spät. Leider wurde @NotTransactional Annotation im Frühling 4 ausgeschlossen, so dass ich meine Tests nur für @Transactional und nicht transaktional trennen. Siehe auch http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/testing.html#integration-testing-annotations von

@NotTransactional is deprecated

UPDATE. 2 Wege-Lösung:

  • trennt Ihr Test explizit für Transactional und Nichttransaktions
  • Verwendung @Transactional(propagation = Propagation.NEVER) wo Transaktion nicht erwünscht ist
+0

Dies ist keine Antwort auf die Frage. Sie können dies als Kommentar zur Antwort hinzufügen oder eine separate Frage zu dieser Frage stellen. –

+0

Ja, ich stimme dir zu. Aber das ist die gute Option für Menschen mit dem gleichen Problem wie Ihnen. – GKislin