Problem z użyciem „prawdziwej” bazy danych do testowania jednostkowego polega na konfiguracji, demontażu i izolacji testów. Nie chcesz rozpędzać całkowicie nowej bazy danych MySQL i tworzyć tabele i dane tylko dla jednego testu jednostkowego. Problemy z tym związane są z zewnętrznym charakterem bazy danych, a testowa baza danych jest wyłączona, testy jednostkowe nie powiodły się. Występują również problemy z upewnieniem się, że masz unikalną bazę danych do testowania. Można je pokonać, ale istnieje prostsza odpowiedź.
Wyśmiewanie bazy danych jest jedną z opcji, jednak nie testuje faktycznie uruchomionych zapytań. Może być stosowany jako znacznie prostsze rozwiązanie, gdy chcesz mieć pewność, że dane z DAO przechodzą prawidłowo przez system. Ale do testowania samego DAO potrzebujesz czegoś, za czym DAO ma dane i zapytania działają poprawnie.
Pierwszą rzeczą do zrobienia jest użycie bazy danych w pamięci. HyperSQL jest doskonałym wyborem do tego celu, ponieważ ma możliwość emulacji dialektu innej bazy danych - dzięki czemu drobne różnice między bazami danych pozostają takie same (typy danych, funkcje i tym podobne). hsqldb ma również kilka fajnych funkcji do testowania jednostek.
db.url=jdbc:hsqldb:file:src/test/resources/testData;shutdown=true;
Spowoduje to załadowanie stanu bazy danych (tabel, danych początkowych) z testData
pliku. shutdown=true
automatycznie zamknie bazę danych po zamknięciu ostatniego połączenia.
Korzystając z wstrzykiwania zależności , niech testy jednostkowe wybierają inną bazę danych niż ta, którą wykorzystują wersje produkcyjna (testowa lub lokalna).
Następnie DAO korzysta z wstrzykniętej bazy danych, dla której można uruchomić testy względem bazy danych.
Testy jednostkowe będą wtedy wyglądały następująco (kilka nudnych rzeczy nie jest zawartych dla zwięzłości):
@Before
public void setUpDB() {
DBConnection connection = new DBConnection();
try {
conn = connection.getDBConnection();
insert = conn.prepareStatement("INSERT INTO data (txt, ts, active) VALUES (?, ?, ?)");
} catch (SQLException e) {
e.printStackTrace();
fail("Error instantiating database table: " + e.getMessage());
}
}
@After
public void tearDown() {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
private void addData(String txt, Timestamp ts, boolean active) throws Exception {
insert.setString(1, txt);
insert.setTimestamp(2, ts);
insert.setBoolean(3, active);
insert.execute();
}
@Test
public void testGetData() throws Exception {
// load data
Calendar time = Calendar.getInstance();
long now = time.getTimeInMillis();
long then1h = now - (60 * 60 * 1000); // one hour ago
long then2m = now - (60 * 1000 * 2); // two minutes ago
addData("active_foo", new Timestamp(then1h), true); // active but old
addData("inactive_bar", new Timestamp(then1h), false); // inactive and old
addData("active_quz", new Timestamp(then2m), true); // active and new
addData("inactive_baz", new Timestamp(then2m), false); // inactive and new
DataAccess dao = new DataAccess();
int count = 0;
for (Data data : dao.getData()) {
count++;
assertTrue(data.getTxt().startsWith("active"));
}
assertEquals("got back " + count + " rows instead of 1", count, 1);
}
I tak masz test jednostkowy, który wywołuje DAO i korzysta z danych skonfigurowanych w bazie danych w locie, która istnieje przez czas trwania testu. Nie musisz martwić się o zasoby zewnętrzne lub stan bazy danych przed uruchomieniem lub przywrócenie do znanego stanu (cóż, „znany stan” to „nie istnieje”, do którego przywrócenie jest banalne).
DBUnit może znacznie uprościć to, co opisałem, w prostszym procesie konfigurowania bazy danych, tworzenia tabel i ładowania danych. Jeśli z jakiegoś powodu będziesz musiał korzystać z rzeczywistej bazy danych, jest to zdecydowanie lepsze narzędzie do użycia.
Powyższy kod jest częścią projektu maven, który napisałem dla potwierdzenia koncepcji TestingWithHsqldb na github