Модульное тестирование временной логики в Java - PullRequest
8 голосов
/ 23 февраля 2012

У меня есть метод, который реализует различную логику для данных, извлекаемых из БД, в зависимости от текущей даты.

Я хочу проверить это, заставив модульный тест создавать объекты, сохранять их в БД и вызывать проверенный метод. Однако для получения предсказуемых результатов мне нужно каждый раз менять системную дату, и я не знаю, как это сделать в Java.

Предложения

Ответы [ 3 ]

15 голосов
/ 23 февраля 2012

Вы можете генерировать ожидаемые результаты, используя текущую дату.

Или вы пишете свою систему, чтобы использовать дату / время, которые вы даете ей при тестировании (а не часы). Таким образом, время всегдатест ожидает.

Я использую что-то вроде

interface TimeSource {
    long currentTimeMS(); // actually I have currentTimeNS
    void currentTimeMS(long currentTimeMS);
}

enum VanillaTimeSource implements TimeSource {
    INSTANCE;

    @Override
    public long currentTimeMS() {
        return System.currentTimeMillis();
    }

    @Override
    public void currentTimeMS(long currentTimeMS) {
        // ignored
    }
}

class FixedTimeSource implements TimeSource {
    private long currentTimeMS;
    @Override
    public long currentTimeMS() {
        return currentTimeMS;
    }

    @Override
    public void currentTimeMS(long currentTimeMS) {
        this.currentTimeMS =              currentTimeMS;
    }
}

В тестах я использую FixedTimeSource, который может управляться данными, например, устанавливаемыми входами / событиями.В производстве я использую VanillaTimeSource.INSTANCE, который игнорирует время во входах / событиях и использует текущее время.

8 голосов
/ 23 февраля 2012

Вам нужно взглянуть на то, что вы вводите в свой класс что-то, что позволяет настроить способ представления времени.

Например

public interface TimeProvider {
   DateTime getCurrentTime();
}

public class UnderTest {

  // Inject this in some way (e.g. provide it in the constructor)
  private TimeProvider timeProvider;

  public void MyMethod() {
     if (timeProvider.getCurrentTime() == "1234") {
        // Do something
     }
  }
}

Теперь в ваших модульных тестах вы можете предоставить поддельную реализацию провайдера времени. В реальном производственном коде вы можете просто вернуть текущую дату и время.

2 голосов
/ 23 февраля 2012

У меня недавно была похожая проблема с кодом, который я не мог слишком много реорганизовать (ограничения по времени, не хотел непреднамеренно что-либо сломать). У него был метод, который я хотел проверить, который назывался System.currentTimeMillis (), и случай, который я хотел проверить, зависел от того, что вернуло это значение. Что-то вроде:

public class ClassINeedToTest {
    public boolean doStuff() {
        long l = System.currentTimeMillis();
        // do some calculation based on l
        // and return the calculation
    }
}

Чтобы разрешить модульное тестирование, я провел рефакторинг класса, чтобы у него был вспомогательный метод, который был защищен

protected long getCurrentTimeMillis() {
     // only for unit-testing purposes
     return System.currentTimeMillis();
}

и этот метод был вызван doStuff (). Это не изменило функциональность, но теперь означало, что когда я вызываю его в модульном тесте, я могу переопределить это, чтобы вернуть определенное значение, например

ClassINeedToTest testClass = new ClassINeedToTest() {
    protected long getCurrentTimeMillis() {
        // return specific date for my test
        return 12456778L;
    }
}; 
boolean result = testClass.doStuff();
// test result with an assert here

Это, однако, означает, что я загрязнил интерфейс моего класса, так что вы можете решить, что цена слишком высока. Вероятно, есть лучшие способы, если вы можете реорганизовать код больше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...