Можно ли измерить время в модульном тесте? - PullRequest
4 голосов
/ 21 августа 2009

Исходя из этого вопроса ... Я пытаюсь выполнить модульное тестирование следующего сценария:

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

Скажем, я хотел вызвать метод DoSomething () ... но в случае возникновения исключения с помощью DoSomething () я хочу иметь возможность повторить вызов до 3 раз, но ждать 1 секунду между каждая попытка. Цель модульного теста, в данном случае, состоит в том, чтобы убедиться, что когда мы вызывали DoSomething () 3 раза с интервалом в 1 секунду между каждой повторной попыткой, общее время выполнения> 3 секунды.

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

  1. тест занимает 3 секунды ... и мне обычно нравится, когда мои тесты выполняются за миллисекунды
  2. время выполнения теста варьируется на +/- 10 мс или около того, что может привести к сбою теста, если я не учту эту разницу.

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

Ответы [ 5 ]

8 голосов
/ 21 августа 2009

Вы можете создать класс Waiter, который предоставляет метод, Wait(int), который ожидает указанное время. Сделайте тесты для этого метода, затем в своем модульном тесте передайте макетированную версию, которая просто отслеживает, как долго его просили ждать, но возвращает сразу.

Например (C #):

interface IWaiter
{
    void Wait(int milliseconds);
}

class Waiter : IWaiter
{
    public void Wait(int milliseconds)
    {
        // not accurate, but for the sake of simplicity:
        Thread.Sleep(milliseconds);
    }
}

class MockedWaiter : IWaiter
{
    public void Wait(int milliseconds)
    {
        WaitedTime += milliseconds;
    }
    public int WaitedTime { get; private set; }
}
4 голосов
/ 21 августа 2009

Хитрость в том, чтобы создать макет провайдера времени.

В Ruby вы просто используете библиотеку насмешек:

сейчас = Time.now Time.expects (: теперь) .returns (сейчас) .once Time.expected (: сейчас). Возвраты (сейчас + 1) .once и т.д.

В Java это немного сложнее. Замените обычное время на ваши собственные "Часы"

interface Clock {
  Date getNow();
}

, а затем переписать:

public String elapsedTime(Date d) {
  final Date now = new Date();
  final long ms = now.getTime() - d.getTime();
  return "" + ms / 1000 + " seconds ago";
}

как:

public String elapsedTime(Date d, Clock clock) {
  final Date now = clock.getNow();
  final long ms = now.getTime() - d.getTime();
  return "" + ms / 1000 + " seconds ago";
}

Очевидно, что Часы можно вводить другими способами, но теперь у нас есть тестируемый и расширяемый метод.

2 голосов
/ 09 октября 2009

Я написал целую серию постов в блоге о тестировании класса таймера в C ++. Как уже говорили другие, ключ состоит в том, чтобы выделить поставщика времени, а затем высмеять его. Как только вы это сделаете, все остальное легко. Смотрите здесь: http://www.lenholgate.com/blog/2004/05/practical-testing.html, если вам интересно.

2 голосов
/ 21 августа 2009

Я часто проверяю такие попытки повторных попыток. Метод, который я использую, состоит в том, чтобы настроить время ожидания - тогда я могу установить его на ноль в моих тестах. В вашем случае вы можете смоделировать объект, который вызывается DoSomething (), ожидать 3 вызова к нему и установить тайм-аут на 0 секунд - тогда вы можете убедиться, что DoSomething () вызывается 3 раза сразу.

Другим способом сделать это было бы иметь интерфейс ITimer, на котором вы вызываете Wait (int секунд) между каждым вызовом DoSomething () - тогда вы можете смоделировать ITimer и убедиться, что Wait (int) вызывается 3 раза с правильный аргумент количества секунд. Конкретная реализация ITimer затем выполняет Thread.sleep или любой другой метод, который вы используете для ожидания.

1 голос
/ 06 сентября 2009

Ваши инстинкты верны. Хитрость заключается в том, чтобы отделить различные части проблемы, а не объединять их в один класс. Вам нужен объект Action, который реализует DoSomething, который не зависит от времени; объект Retryer, который будет управлять попытками вызвать объект Action; и часы, которые ждут. Вы можете выполнить модульное тестирование объекта «Действие» напрямую, модульное тестирование «Ретриллера» с помеченными объектами «Часы» и «Действие» и сделать Часы такими простыми, чтобы они просто работали.

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

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