sobota, 5 stycznia 2013

JUnit rule i mockowanie new Date() cz.1

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.

1 komentarz:

  1. Many advantages come with partnering with a metallic fabrication service firm. Multiple processes during production add further production hours and prices. Companies like authentic equipment manufacturers can benefit lots from the companies provided by sheet metallic fabrication retailers. We use 3D modeling, permitting us to incorporate structural components and sheet metallic into complex assemblies. Is one of the most widely-used fabrication methods, due to its effectivity and efficacy. CNC machining uses Mittens for Extreme Cold automated chopping tools to exactly take away materials from a workpiece until it matches the desired shape.

    OdpowiedzUsuń