Как разработать тест класса StopWatch в первую очередь?

В настоящее время я пытаюсь реализовать StopWatch учебный класс. Интерфейс примерно такой:

interface IStopWatch {
    void Run();
    void Stop();
    int SecondsElapsed { get; }
    int MinutesElapsed { get; }
}

В основном мое приложение должно будет использовать StopWatch, но для целей тестирования было бы неплохо иметь способ искусственного изменения StopWatchРезультаты ES, поэтому я делаю ссылку на весь код IStopWatch вместо.NET System.Stopwatch,

Поскольку я пытаюсь разработать этот Test-First, мне придется сделать код для моего StopWatch Класс только после написания своих тестов. Я уже понял, что тесты, которые я собираюсь сделать, не будут модульными, так как я использую.NET System.Stopwatch класс внутри.

Итак, по моему заданию, единственное, что я могу сделать сейчас, это тесты, которые выглядят следующим образом:

[TestMethod]
public void Right_After_Calling_Run_Elapsed_Minutes_Equals_Zero() {
    IStopWatch stopWatch = new StopWatch();
    stopWatch.Run();
    Assert.AreEqual<int>(0, stopWatch.ElapsedMinutes);
}

[TestMethod]
public void One_Second_After_Run_Elapsed_Seconds_Equals_One() {
    IStopWatch stopWatch = new StopWatch();
    stopWatch.Run();
    Thread.Sleep(1000 + 10);
    Assert.AreEqual<int>(1, stopWatch.ElapsedSeconds);
}

[TestMethod]
public void Sixty_Seconds_After_Run_Elapsed_Minutes_Equals_One() {
    IStopWatch stopWatch = new StopWatch();
    stopWatch.Run();
    Thread.Sleep(60 * 1000 + 1000);
    Assert.AreEqual<int>(1, stopWatch.ElapsedMinutes);            
}

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

Спасибо

редактировать

Поэтому я в итоге последовал советам Quinn351 и Sjoerd и написал нечто, использующее DateTimes вместо класса секундомера.NET:

public class MyStopWatch : IMyStopWatch {
    private DateTime? firstDateTime = null;
    private DateTime? secondDateTime = null;
    private bool isRunning = false;

    public void Start() {
        isRunning = true;
        firstDateTime = DateTime.Now;
    }

    public void Stop() {
        isRunning = false;
        secondDateTime = DateTime.Now;
    }

    public void Reset() {
        firstDateTime = null;
        secondDateTime = null;
        isRunning = false;
    }

    public TimeSpan GetTimeElapsed() {
        return secondDateTime.Value.Subtract(firstDateTime.Value);
    }
}

Это позволяет мне делать другие реализации, которые имеют getter/setters для обеих дат, поэтому я могу заставить GetTimeElapsed() возвращать все, что я хочу.

4 ответа

Решение

Ваш класс секундомера, вероятно, откуда-то получает дату и время. Сделать объект, который возвращает текущую дату. Это был бы очень простой класс. При тестировании вместо этого передайте фиктивный объект, который возвращает фиктивную дату и время. Таким образом, вы можете притворяться в своем тесте, как будто прошло много секунд.

class DateTime {
    public Date getDate() {
        return System.DateTime.Now();
    }
}

class MockDateTime {
    public Date getDate() {
        return this.fakeDate;
    }
}

class StopwatchTest {
    public void GoneInSixtySeconds() {
        MockDateTime d = new MockDateTime();
        d.fakeDate = '2010-01-01 12:00:00';
        Stopwatch s = new Stopwatch();
        s.Run();
        d.fakeDate = '2010-01-01 12:01:00';
        s.Stop();
        assertEquals(60, s.ElapsedSeconds);
    }
}

Зачем "искусственно модифицировать результаты StopWatches"?! Какова цель тестирования, если вы собираетесь изменить свои результаты?

Что касается тестирования, вы должны также протестировать метод stop и проверить, является ли количество секунд примерно в 60 раз больше количества минут.

Класс, который вы пишете, может также использовать StopWatch для внутреннего использования, а затем изменять результаты по запросу (тогда вы всегда можете вернуться к исходным результатам).

PS: имена методов должны быть короче и не иметь подчеркивания.

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

C# Секундомер показывает неправильное время

Поэтому использование Thread.Sleep не даст вам точного времени. Вы бы лучше реализовать свою собственную версию секундомера, используя DateTime.Now

В дополнение к EDIT выше: не используйте этот метод для определения истекшего времени! Это не работает хорошо, когда:

  • Элемент списка
  • часы или часовой пояс устанавливаются
  • переход на летнее время начинается или заканчивается
  • часы иначе ненадежны (например, внутри виртуальной машины)

... и, возможно, ряд других условий.

Другие вопросы по тегам