Jeśli większość twoich usług po prostu przechodzi do twoich daos, a twoi daos robią niewiele, ale wywołują metody na Spring HibernateTemplate
lub JdbcTemplate
to masz rację, że testy jednostkowe nie dowodzą niczego, co już udowodniłeś w testach integracyjnych. Jednak posiadanie testów jednostkowych jest cenne ze wszystkich zwykłych powodów.
Ponieważ testy jednostkowe przetestować tylko pojedyncze zajęcia, prowadzone w pamięci bez dysku lub sieci dostępowej i nigdy naprawdę przetestować wiele klas pracujących razem, normalnie iść tak: testy jednostkowe
- serwisowe mock DAOs.
- Testy jednostkowe typu Dao pozorują sterownik bazy danych (lub szablon sprężyny) lub korzystają z wbudowanej bazy danych (bardzo łatwe na wiosnę 3).
Do testów jednostkowych usługa, która właśnie przechodzi do dao, można drwić tak:
@Before
public void setUp() {
service = new EventServiceImpl();
dao = mock(EventDao.class);
service.EventDao = dao;
}
@Test
public void creationDelegatesToDao() {
service.createEvent(sampleEvent);
verify(dao).createEvent(sampleEvent);
}
@Test(expected=EventExistsException.class)
public void creationPropagatesExistExceptions() {
doThrow(new EventExistsException()).when(dao).createEvent(sampleEvent);
service.createEvent(sampleEvent);
}
@Test
public void updatesDelegateToDao() {
service.updateEvent(sampleEvent);
verify(dao).updateEvent(sampleEvent);
}
@Test
public void findingDelgatesToDao() {
when(dao.findEventById(7)).thenReturn(sampleEvent);
assertThat(service.findEventById(7), equalTo(sampleEvent));
service.findEvents("Alice", 1, 5);
verify(dao).findEventsByName("Alice", 1, 5);
service.findEvents(null, 10, 50);
verify(dao).findAllEvents(10, 50);
}
@Test
public void deletionDelegatesToDao() {
service.deleteEvent(sampleEvent);
verify(dao).deleteEvent(sampleEvent);
}
Ale jest to naprawdę dobry pomysł? Te twierdzenia Mockito potwierdzają, że metoda dao została wywołana, a nie, że zrobiła to, co oczekiwano! Otrzymasz numery ubezpieczenia, ale mniej lub bardziej wiążą twoje testy do wdrożenia dao. Oooo.
Ten przykład zakłada, że usługa nie miała prawdziwej logiki biznesowej. Zwykle usługi będą miały logikę biznesową oprócz wywołań dao, a ty na pewno musisz je przetestować.
Teraz, dla testów jednostkowych, lubię korzystać z wbudowanej bazy danych.
private EmbeddedDatabase database;
private EventDaoJdbcImpl eventDao = new EventDaoJdbcImpl();
@Before
public void setUp() {
database = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql")
.addScript("init.sql")
.build();
eventDao.jdbcTemplate = new JdbcTemplate(database);
}
@Test
public void creatingIncrementsSize() {
Event e = new Event(9, "Company Softball Game");
int initialCount = eventDao.findNumberOfEvents();
eventDao.createEvent(e);
assertThat(eventDao.findNumberOfEvents(), is(initialCount + 1));
}
@Test
public void deletingDecrementsSize() {
Event e = new Event(1, "Poker Night");
int initialCount = eventDao.findNumberOfEvents();
eventDao.deleteEvent(e);
assertThat(eventDao.findNumberOfEvents(), is(initialCount - 1));
}
@Test
public void createdEventCanBeFound() {
eventDao.createEvent(new Event(9, "Company Softball Game"));
Event e = eventDao.findEventById(9);
assertThat(e.getId(), is(9));
assertThat(e.getName(), is("Company Softball Game"));
}
@Test
public void updatesToCreatedEventCanBeRead() {
eventDao.createEvent(new Event(9, "Company Softball Game"));
Event e = eventDao.findEventById(9);
e.setName("Cricket Game");
eventDao.updateEvent(e);
e = eventDao.findEventById(9);
assertThat(e.getId(), is(9));
assertThat(e.getName(), is("Cricket Game"));
}
@Test(expected=EventExistsException.class)
public void creatingDuplicateEventThrowsException() {
eventDao.createEvent(new Event(1, "Id1WasAlreadyUsed"));
}
@Test(expected=NoSuchEventException.class)
public void updatingNonExistentEventThrowsException() {
eventDao.updateEvent(new Event(1000, "Unknown"));
}
@Test(expected=NoSuchEventException.class)
public void deletingNonExistentEventThrowsException() {
eventDao.deleteEvent(new Event(1000, "Unknown"));
}
@Test(expected=NoSuchEventException.class)
public void findingNonExistentEventThrowsException() {
eventDao.findEventById(1000);
}
@Test
public void countOfInitialDataSetIsAsExpected() {
assertThat(eventDao.findNumberOfEvents(), is(8));
}
Nadal nazywam to testem jednostkowym, chociaż większość ludzi może nazwać to testem integracyjnym.Wbudowana baza danych znajduje się w pamięci i jest rozwijana i usuwana po uruchomieniu testów. Jest to jednak zależne od tego, czy osadzona baza danych wygląda tak samo, jak baza danych produkcji. Czy tak właśnie będzie? Jeśli nie, to cała ta praca była całkiem bezużyteczna. Jeśli tak, to, jak mówisz, testy te robią coś innego niż testy integracyjne. Ale mogę je uruchomić na żądanie z mvn test
i mam zaufanie do refactor.
Dlatego piszę te testy jednostkowe i spełniają moje cele dotyczące zasięgu. Podczas pisania testów integracji twierdzę, że żądanie HTTP zwraca oczekiwaną odpowiedź HTTP. Tak, to podciąga testy jednostkowe, ale hej, kiedy ćwiczysz TDD, masz testy jednostkowe napisane przed twoją rzeczywistą implementacją dao.
Jeśli piszesz testy jednostkowe po dao, to oczywiście nie są one zabawne w pisaniu. Literatura TDD jest pełna ostrzeżeń na temat tego, jak pisanie testów po twoim kodzie wydaje się działać i nikt nie chce tego robić.
TL; DR: Twoje testy integracyjne będą obejmować testy jednostkowe iw tym sensie testy jednostkowe nie dodają rzeczywistej wartości testowej. Jednak, gdy masz pakiet testów jednostkowych o dużym zasięgu, masz pewność, że refactor. Ale oczywiście, jeśli dao trywialnie nazywa szablon dostępu do danych Spring, to może nie być refaktoryzacji. Ale nigdy nie wiesz. I na koniec, jeśli testy jednostkowe zostaną napisane najpierw w stylu TDD, i tak je otrzymasz.
Ray, bardzo dziękuję za szczegółowe wyjaśnienia i przykłady! Sądzę, że najpierw będziemy trzymać się podejścia do pisania testów integracyjnych. Posunąłbym się nawet do stwierdzenia, że testy jednostkowe usług CRUD aplikacji internetowej są niezłe, ale tak naprawdę liczy się test integracyjny. i dlaczego nie robić tego w trybie TDD, ale zamiast testów jednostkowych pisać testy integracyjne przed wdrożeniem usługi? – fischermatte
To będzie dobrze; policja testów jednostkowych nie zapuka do twoich drzwi. Prawdą jest, że testy integracyjne utworzą testy jednostkowe.Osobiście cieszę się, że mam tam testy jednostkowe (pewność refaktoryzacji, poczucie kompletności i tak dalej), ale to tylko ja. Jeśli jesteś zadowolony z uzyskania zasięgu dzięki testom integracyjnym, a nie testom jednostkowym, a testy integracyjne są szybkimi testami programistycznymi generowanymi przez programistów, w przeciwieństwie do generowanych przez QA testów funkcjonalno-integracyjnych, testów akceptacyjnych i produkcyjno-produkcyjnych, a następnie cool. :) –