Плохо ли юнит-тестирование действий с временным интервалом с Thread.Sleep? - PullRequest
7 голосов
/ 03 февраля 2012

Если у меня есть испытуемый объект с таймером, который заставляет выполнять какое-то действие через определенный интервал времени, каков хороший способ проверить это?

Один метод заключается в том, чтобы обернуть таймер в интерфейс и внедрить его как зависимость.

Однако я бы хотел избежать создания еще одной абстракции. Кажется, я могу избежать этого, вводя интервал обновления, а не таймер. Затем в моем тесте (в предположении стиля тестирования AAA) я поставил Thread.Sleep после Act и до Assert , используя очень маленькое значение времени, чтобы тест не занимал долго бежать.

Это плохая идея? Я знаю, что это, вероятно, не полностью следует принципам TDD, но, похоже, должна быть линия, где вы перестанете окружать все контрактом и вводить его.

Ответы [ 2 ]

6 голосов
/ 03 февраля 2012

Если количество сна, которое вы спите, не имеет никакого значения в тесте, и вы можете установить i равным 1 миллисекунде, тогда может быть достаточно просто спать в течение 1 миллисекунды в вашем тесте.

Однако, если вы хотите протестировать сложные временные характеристики с таймаутами и конкретными действиями, предпринимаемыми в определенные моменты времени, быстро становится проще абстрагировать понятие времени и внедрить его как зависимость. Тогда ваши тесты могут работать в виртуальном времени и выполняться без задержки, даже если код работает так, как если бы проходило реальное время.

Простой способ виртуализации времени - использовать что-то вроде этого:

interface ITimeService {

  DateTime Now { get; }

  void Sleep(TimeSpan delay);

}

class TimeService : ITimeService {

  public DateTime Now { get { return DateTime.UtcNow; } }

  public void Sleep(TimeSpan delay) { Thread.Sleep(delay); }

}

class TimeServiceStub : ITimeService {

  DateTime now;

  public TimeServiceStub() {
    this.now = DateTime.UtcNow;
  }

  public DateTime Now { get { return this.now; } }

  public void Sleep(TimeSpan delay) {
    this.now += delay;
  }

}

Вам придется расширить эту идею, если вам требуется более реактивное поведение, такое как срабатывание таймеров и т. Д.

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

Внедрение зависимостей - это способ полностью избежать «тестового» кода в вашем производственном коде (например, установить интервал только для модульного тестирования).

Однако в этом случае я бы использовал заданный интервальный код, но использовал бы его как в модульных тестах, так и в производстве. Устанавливают его на что угодно, а юнит-тесты устанавливают очень небольшое количество (10 мс?). Тогда у вас не будет никакого мертвого кода, слоняющегося в производстве.

Если вы установите интервал, я не понимаю, зачем вам нужен Thread.Sleep? Просто проведите блок юнит-теста, пока не получите событие от субъекта (или не проводите непрерывный опрос субъекта). Какой бы метод вы ни использовали.

...