Я должен протестировать метод, который делает определенное количество работы после интервала.
while (running)
{
...
// Work
...
Thread.Sleep(Interval);
}
Интервал передается в в качестве параметра классу, таким образом, я могу просто передать в 0 или 1, но мне было интересно относительно того, как дразнить системные часы, если это не имело место.
В моем тесте я хотел бы смочь просто установить время вперед Интервалом TimeSpan и имел бы поток, просыпаются.
Я никогда не писал тесты для кода, который реагирует на выполняющийся поток прежде, и я уверен, что существуют некоторые ловушки для предотвращения - не стесняйтесь уточнять, на каком подходе Вы используете.
Спасибо!
Если вы не хотите проверять тот факт, что поток действительно спит, то более простым подходом (и возможным) является наличие ISleepService. Затем вы можете поиздеваться над ним, и тогда он не будет спать в ваших тестах, но будет иметь реализацию, которая вызовет Thread.Sleep в вашем производственном коде.
ISleepService sleepService = Container.Resolve<ISleepService>();
..
while (running)
{
...
// Work
...
sleepService.Sleep(Interval);
}
Пример с использованием Moq:
public interface ISleepService
{
void Sleep(int interval);
}
[Test]
public void Test()
{
const int Interval = 1000;
Mock<ISleepService> sleepService = new Mock<ISleepService>();
sleepService.Setup(s => s.Sleep(It.IsAny<int>()));
_container.RegisterInstance(sleepService.Object);
SomeClass someClass = _container.Resolve<SomeClass>();
someClass.DoSomething(interval: Interval);
//Do some asserting.
//Optionally assert that sleep service was called
sleepService.Verify(s => s.Sleep(Interval));
}
private class SomeClass
{
private readonly ISleepService _sleepService;
public SomeClass(IUnityContainer container)
{
_sleepService = container.Resolve<ISleepService>();
}
public void DoSomething(int interval)
{
while (true)
{
_sleepService.Sleep(interval);
break;
}
}
}
Обновление
По поводу проектирования и обслуживания, если вам больно менять конструктор "SomeClass", или добавлять точки Dependency Injection для пользователя класса, то здесь может помочь паттерн типа локатора сервиса, например:
private class SomeClass
{
private readonly ISleepService _sleepService;
public SomeClass()
{
_sleepService = ServiceLocator.Container.Resolve<ISleepService>();
}
public void DoSomething(int interval)
{
while (true)
{
_sleepService.Sleep(interval);
break;
}
}
}
Вы не можете издеваться над системными часами.
Если вам нужно изменить поведение кода при приостановке, подобным этому, вам нужно будет его реорганизовать, чтобы вы не вызывали Thread.Sleep ()
напрямую.
Я бы создал одноэлементную службу, которую можно было бы внедрить в приложение, когда оно тестируется. Одноэлементная служба должна включать методы, позволяющие некоторому внешнему вызывающему объекту (например, модульному тесту) отменить операцию сна.
В качестве альтернативы вы можете использовать метод WaitOne ()
объекта Mutex
или WaitHandle
, у которого есть параметр тайм-аута. Таким образом, вы можете запустить мьютекс для отмены "сна" или дать ему время ожидания:
public WaitHandle CancellableSleep = new WaitHandle(); // publicly available
// in your code under test use this instead of Thread.Sleep()...
while( running ) {
// .. work ..
CancellableSleep.WaitOne( Interval ); // suspends thread for Interval timeout
}
// external code can cancel the sleep by doing:
CancellableSleep.Set(); // trigger the handle...