Как разработать тест класса 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 выше: не используйте этот метод для определения истекшего времени! Это не работает хорошо, когда:
- Элемент списка
- часы или часовой пояс устанавливаются
- переход на летнее время начинается или заканчивается
- часы иначе ненадежны (например, внутри виртуальной машины)
... и, возможно, ряд других условий.