Michał Borek

Tech blog

Testowanie ze Springiem – nowa porcja wiedzy

Chciałbym napisać nieco więcej na temat testowania w Springu i przytoczę tutaj kilka luźno związanych, ale wartych zapamiętania reguł.

Przede wszystkim, kiedy tworzymy testy integracyjne i tworzymy kontekst aplikacji musimy wiedzieć, że kontekst ten tworzony jest raz na cały cykl testów. Jest to ważne, gdyż czasem stan naszych beanów może się zmienić podczas testów i może to powodować nieprawidłowe wykonywanie kolejnych testów. Aby zapobiec temu problemowi wykorzystujemy dodatkową adnotację:

@DirtiesContext – adnotacja do metody, która określa, że konspekt aplikacji mógł zostać zmieniony przez test i musi zostać konspekt musi zostać stworzony od nowa.

Ogólnie sam fakt, że kontekst tworzony jest raz jest bardzo korzystny dla nas, gdyż skraca to znacznie czas wykonywania testów.

Poniżej opiszę jeszcze 2 ciekawe adnotacje, które znacznie rozszerzają możliwości testów.

@Timed(millis=1000) – która, jak można się domyślić, określa w jakim maksymalnym czasie, test ma zostać wykonany. Jeżeli test wykonuje się dłużej, nie jest on spełniony.

@Repeat(10) – test wykonywany jest wielokrotnie (w tym wypadku 10 razy) i tylko w przypadku, gdy wszystkie 10 testów przejdzie, test zostanie spełniony.

Nie jest to cała lista adnotacji, jednakże są to najciekawsze (wraz z tymi z poprzedniego wpisu) wg mojego uznania.

Spring Framework testowanie DAO z użyciem adnotacji

Ostatnimi czasy siedzę trochę w Spring Framework, frameworku dla Javy. Doszedłem do etapu testów i stwierdziłem, że godnym opisania będzie sposób testowania metod opartych o transakcje, gdyż ciekawym jest fakt iż wszystko co odbywa się w danych testach (dodawanie danych, edycja itd.) jest następnie cofane do wersji pierwotnej (wywoływany jest rollback na transakcji) i dzieje się to automatycznie.

Poniżej znajduje się przykładowa klasa wraz z metodami testującymi. Wszystko oparte jest na adnotacjach, więc nie ma problemu:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:test-config.xml"
public class UserAccountDaoImplTest {

    @Resource
    UserAccountDaoImpl userAccountDao;

    @Test
    @Transactional
    public void testSaveUser() {
        UserAccount transientInstance = new UserAccount();
        transientInstance.setEmail("email@mail.con");
        transientInstance.setEnabled(true);
        userAccountDao.saveUser(transientInstance);
        UserAccount userFromDb = userAccountDao.getByEmail("email@mail.con);
        assertNotNull("User should be not null", userFromDb);
        assertTrue("User should be enabled", userFromDb.isEnabled());
        assertTrue("User should no be locked", userFromDb.isAccountNonLocked());
    }

    @Test(expected = ConstraintViolationException.class)
    @Transactional
    public void testSaveUserDuplicateEmail() {
        System.out.println("saveUserDuplicateEmail");
        UserAccount transientInstance = new UserAccount();
        transientInstance.setEmail("michal@somedomain.pl");
        userAccountDao.saveUser(transientInstance);
    }

    @Test
    @NotTransactional
    public void testGetUserById() {
        long id = 1L;
        UserAccount result = userAccountDao.getUserById(id);
        assertNotNull("User with id " + id + " should be found", result);
        assertEquals(new Long(id), result.getId())        id = 667L;
        result = userAccountDao.getUserById(id);
        assertNull("User with id " + id + " should not be found", result);
    }

    @Test(expected = UsernameNotFoundException.class)
    @NotTransactional
    public void testGetByEmailNoUser() {
        System.out.println("getByEmailNoUser");
        String email = "someone@gnail.con";
        userAccountDao.getByEmail(email);
    }
}

Początkowo inicjujemy kontekst springa (definiujemy która klasa się tym zajmie i dodajemy plik konfiguracyjny).

1
2
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = classpath:test-config.xml)

Następnie musimy wstrzyknąć do testu instancję którą testujemy, wykorzystujemy do tego adnotację @Resource:

5
6
@Resource
UserAccountDaoImpl userAccountDao;

Nie będę tutaj opisywał adnotacji czystego JUnita, zwrócę jedynie uwagę na adnotacje potrzebne przy testowaniu danych opartych o DB. Tak więc mamy:

@Transactional – określa nam, że dana metoda korzysta z transakcji i ma być wykonany rollback @NotTransactional – odwrotnie do tego wyżej

Dodatkowo, co nie zostało pokazane na powyższym przykładzie, możemy skorzystać z adnotacji @Rollback(false) jeżeli nie chcemy, aby wykonywany był rollback po wykonaniu testu.

Mam nadzieję, że powyższy (działający u mnie) przykład komuś się przyda. Ja chwilę spędziłem, zanim doprowadziłem go do porządku, choć okazało się to niezwykle proste (wynik końcowy),