Mam wrażenie, że temat został już dawno rozpracowany, ale niektórzy nadal zapominają, że pisząc taki test:
@Test
public void shouldCreateOrderWithCurrentCreateDate() {
//given
Order order = new Order();
//when
Date createDate = order.getCreateDate();
//then
assertThat(createDate).isEqualTo(new Date());
}
dla takiej klasy:
public class Order {
private Date createDate;
public Order() {
this.createDate = new Date();
}
public Date getCreateDate() {
return createDate;
}
}
będzie on bardzo niedeterministyczny i raz zadziała a raz nie. Dotychczas, w celu pozbycie się problemu, zamiast new Date(), wywoływałem statyczną metodę z JodaTime (
this.createDate = DateTime.now().toDate();), którą następnie mockowałem za pomocą PowerMockito. I test mógł wyglądać tak:
@Test
public void shouldCreateOrderWithCurrentCreateDate() {
//given
DateTime currentDate = new DateTime();
PowerMockito.mockStatic(DateTime.class);
PowerMockito.when(DateTime.now()).thenReturn(currentDate);
Order order = new Order();
//when
Date createDate = order.getCreateDate();
//then
assertThat(createDate).isEqualTo(currentDate.toDate());
}
Minusem takiego podejścia jest fakt, że rezerwujemy sobie runnera na PowerMocka i już innego nie będziemy mogli użyć, np. dla testów w kontekście springa. Żeby nie wiało nudą, zainspirowany ostatnim szkoleniem TDD z
Rafełem Jamrózem, proponuje inne podejście, a konkretnie wykorzystanie junitowych
@Rule. Mechanizm ten jest alternatywą dla @Before i @After, czyli poprzez implementację odpowiedniego interfejsu pozwala na zrobienie czegoś przed i po metodzie testowej. Dlaczego jest to lepsze od @Before i @After - ponieważ raz zaimplementowane, może być używane potem w wielu testach
bez dziedziczenia i bez copy-paste.
Korzystając z JodaTime mamy do dyspozycji utilsa do sterowania źródłem aktualnego czasu. Implementacja klasy sterującej czasem w testach może wyglądać następująco:
public class Clock extends ExternalResource {
private static DateTime currentTime;
private Clock() {}
public static Clock standard() {
return new Clock();
}
@Override
protected void before() throws Throwable {
currentTime = new DateTime();
}
@Override
protected void after() {
DateTimeUtils.setCurrentMillisSystem();
}
/**
* set date and stop the clock
*/
public void setFixedTime(Date date) {
currentTime = new DateTime(date);
DateTimeUtils.setCurrentMillisFixed(currentTime.getMillis());
}
public DateTime getCurrentDateTime() {
return currentTime;
}
public Date getCurrentDate() {
return currentTime.toDate();
}
}
A test z wykorzystaniem rula:
@Rule
public Clock clock = Clock.standard();
@Test
public void shouldCreateOrderWithCurrentCreateDate() {
// given
Date date = new Date();
clock.setFixedTime(date);
Order order = new Order();
// when
Date createDate = order.getCreateDate();
// then
assertThat(createDate).isEqualTo(date);
}
Należy tylko pamiętać, że
do tworzenia daty w zamówieniu
używamy JodaTime. Implementacja Clocka nie wpływa na inne metody testowe (patrz. metoda after()), więc możemy w zależności od potrzeb korzystać z jego funkcjonalności lub nie.
Brak komentarzy:
Prześlij komentarz