Transakcja wycofania po @Test

85

Po pierwsze, znalazłem wiele wątków na ten temat w StackOverflow, ale żaden z nich tak naprawdę mi nie pomógł, więc przepraszam, że zadaję prawdopodobnie zduplikowane pytanie.

Przeprowadzam testy JUnit za pomocą testu sprężynowego, mój kod wygląda następująco

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = {})
public class StudentSystemTest {

    @Autowired
    private StudentSystem studentSystem;

    @Before
    public void initTest() {
    // set up the database, create basic structure for testing
    }

    @Test
    public void test1() {
    }    
    ...  
}

Mój problem polega na tym, że chcę, aby moje testy NIE wpływały na inne testy. Chciałbym więc stworzyć coś takiego jak wycofywanie zmian dla każdego testu. Szukałem dużo tego, ale jak dotąd nic nie znalazłem. Używam do tego Hibernate i MySql

Jan Vorcak
źródło
Co masz na myśli przez wycofanie? Czyszczenie bazy danych?
Gaurav
5
ustawienie go w dokładnie takim samym stanie, w jakim był po wykonaniuinitTest
Jan Vorcak

Odpowiedzi:

127

Po prostu dodaj @Transactionaladnotację na górze testu:

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = {"testContext.xml"})
@Transactional
public class StudentSystemTest {

Domyślnie Spring rozpocznie nową transakcję otaczającą twoją metodę testową i @Before/ @Aftercallbacki, wycofując na koniec. Domyślnie działa, wystarczy mieć w kontekście jakiegoś menedżera transakcji.

Od: 10.3.5.4 Zarządzanie transakcjami (moje pogrubienie):

W strukturze TestContext transakcje są zarządzane przez TransactionalTestExecutionListener. Zauważ, że TransactionalTestExecutionListenerjest to skonfigurowane domyślnie , nawet jeśli nie deklarujesz jawnie @TestExecutionListenersswojej klasy testowej. Aby jednak umożliwić obsługę transakcji, należy podać PlatformTransactionManagerkomponent bean w kontekście aplikacji ładowanym przez @ContextConfigurationsemantykę. Ponadto w przypadku testów należy zadeklarować @Transactionalna poziomie klasy lub metody .

Tomasz Nurkiewicz
źródło
2
cóż, próbowałem tego wcześniej i nadal nie działa, może ... czy problem polega na tym, że nie zdefiniowałem PlatformTransactionManager, jak mam to zrobić?
Jan Vorcak
@javo: jak modyfikujesz bazę danych? Jeśli używasz Jpa / Hibernate / JdbcTemplate / ... musi być jakiś plik PlatformTransactionManager. W przeciwnym razie skąd Spring będzie wiedzieć o Twoich transakcjach i bazie danych?
Tomasz Nurkiewicz
1
Link w tej odpowiedzi nie jest już poprawny; zobacz odpowiedź user2418306 poniżej, aby uzyskać poprawny link i więcej kontekstu z dokumentacji Spring.
DaveyDaveDave
1
Nie działa, każda metoda wymaga osobnej transakcji, nie mogę tego zrobić dla całej klasy
Kamil Nekanowicz
Testuję wkładkę do stołu. W przypadku @Transactional polecenie wstawiania nie jest nawet wydawane względem bazy danych (widzę to, ponieważ konsola ma hibernację show-sql true). W wielu przypadkach działa dobrze. Ale jeden z moich testów sprawdza, czy baza danych wystawia wyjątek DataAccessException z powodu ograniczenia bazy danych. W tym przypadku test kończy się niepowodzeniem, ponieważ wycofywanie zmian następuje „w pamięci”, a baza danych nigdy nie jest wywoływana. Rozwiązanie: używaj @Transactionalna @Testpoziomie metody, a nie na poziomie klasy.
Paulo Merson
17

Na boku: odrzucono próbę zmiany odpowiedzi Tomasza Nurkiewicza:

Ta zmiana nie czyni posta nawet odrobinę bardziej czytelnym, łatwiejszym do znalezienia, dokładniejszym ani bardziej dostępnym. Zmiany są albo całkowicie zbędne, albo aktywnie szkodzą czytelności.


Prawidłowy i stały link do odpowiedniej sekcji dokumentacji dotyczącej testów integracyjnych.

Aby włączyć obsługę transakcji, należy skonfigurować PlatformTransactionManagerkomponent bean w ApplicationContextładowanym za pośrednictwem @ContextConfigurationsemantyki.

@Konfiguracja
@PropertySource ("application.properties")
public class Persistence {
    @Autowired
    Środowisko env;

    @Fasola
    DataSource dataSource () {
        zwraca nowe DriverManagerDataSource (
                env.getProperty ("datasource.url"),
                env.getProperty ("datasource.user"),
                env.getProperty ("datasource.password")
        );
    }

    @Fasola
    PlatformTransactionManager transactionManager () {
        zwraca nowe DataSourceTransactionManager (dataSource ());
    }
}

Ponadto musisz zadeklarować @Transactionaladnotację Springa na poziomie klasy lub metody dla swoich testów.

@RunWith (SpringJUnit4ClassRunner.class)
@ContextConfiguration (classes = {Persistence.class, SomeRepository.class})
@Transactional
public class SomeRepositoryTest {...}

Dodanie adnotacji do metody testowej @Transactionalpowoduje, że test jest uruchamiany w ramach transakcji, która domyślnie zostanie automatycznie wycofana po zakończeniu testu. Jeśli klasa testowa jest opatrzona adnotacją @Transactional, każda metoda testowa w ramach tej hierarchii klas zostanie uruchomiona w ramach transakcji.

user2418306
źródło
12

Odpowiedzi dotyczące dodawania @Transactionalsą poprawne, ale dla uproszczenia możesz po prostu mieć swoją klasę testową extends AbstractTransactionalJUnit4SpringContextTests.

matowe b
źródło
1
dodanie adnotacji „@Transactional” na poziomie klasy nie działa, dodanie adnotacji „@Transactional” osobno dla każdej funkcji działa i rozszerza AbstractTransactionalJUnit4SpringContextTests
Kamil Nekanowicz
5

Wiem, jestem za późno, aby opublikować odpowiedź, ale mam nadzieję, że może to komuś pomóc. Poza tym właśnie rozwiązałem ten problem, który miałem z moimi testami. Oto, co miałem w moim teście:

Moja klasa testowa

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "path-to-context" })
@Transactional
public class MyIntegrationTest 

Context xml

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
   <property name="driverClassName" value="${jdbc.driverClassName}" />
   <property name="url" value="${jdbc.url}" />
   <property name="username" value="${jdbc.username}" />
   <property name="password" value="${jdbc.password}" />
</bean>

Nadal miałem problem, że baza danych nie była czyszczona automatycznie.

Problem został rozwiązany, gdy dodałem następującą właściwość do BasicDataSource

<property name="defaultAutoCommit" value="false" />

Mam nadzieję, że to pomoże.

Atul Kumbhar
źródło
Cóż, więc zatwierdzasz swoje oświadczenia ręcznie? Czy na pewno Twoje dane zostały w ogóle zapisane w Twojej bazie danych?
franta kocourek
Dla każdego, kto ma problemy ze zrozumieniem Spring Transactions, upewnij się, że źródło danych NIE jest ustawione na automatyczne zatwierdzanie, w przeciwnym razie doprowadzisz się do szaleństwa, próbując zrozumieć, dlaczego @Transactional wydaje się nic nie robić.
Joe Ernst
2

Musisz uruchomić swój test z kontekstem Springa i menedżerem transakcji, np.

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = {"/your-applicationContext.xml"})
@TransactionConfiguration(transactionManager="txMgr")
public class StudentSystemTest {

     @Test
     public void testTransactionalService() {
         // test transactional service
     }

     @Test
     @Transactional
     public void testNonTransactionalService() {
         // test non-transactional service
     }
}

Więcej informacji można znaleźć w rozdziale 3.5.8. Transaction ManagementŹródła wiosenne.

Johan Sjöberg
źródło
0

Oprócz dodawania @Transactionalna @Testmetody, trzeba także dodać@Rollback(false)

user2615724
źródło
-4

Możesz wyłączyć wycofywanie zmian:

@TransactionConfiguration(defaultRollback = false)

Przykład:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@Transactional
@TransactionConfiguration(defaultRollback = false)
public class Test {
    @PersistenceContext
    private EntityManager em;

    @org.junit.Test
    public void menge() {
        PersistentObject object = new PersistentObject();
        em.persist(object);
        em.flush();
    }
}
DwD
źródło
6
To jest dokładnie odwrotnie, o co prosi PO
Adam Michalik
To powinien być komentarz do zaakceptowanej odpowiedzi.
DerMike