Код модульного тестирования, который не выполняется сразу - PullRequest
2 голосов
/ 10 марта 2009

Я использую C # 3.0 и NUnit. Мне интересно, есть ли стандартный способ выполнения модульных тестов кода, который выполняется через некоторое время. Например, у меня есть простой статический класс, с помощью которого я могу зарегистрировать методы, и они будут вызваны через n миллисекунд. Я должен убедиться, что код в методах делегата вызывается.

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

[Test]
public void SampleTest()
{
    IntervalManager.SetTimeout(delegate{ 
        Assert.Equals(now.Millisecond + 100, DateTime.Now.Millisecond); 
    }, 100);
}

Возможно ли даже выполнить модульный тестовый код, который выполняется не сразу?

Приветствия

Пол

Ответы [ 7 ]

2 голосов
/ 07 мая 2009

В качестве примечания. Мы обычно пытаемся пометить наши медленные тесты категорией NUnit и можем пропустить эти тесты в некоторых сборках.

2 голосов
/ 10 марта 2009

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

public void Foo() {
    AutoResetEvent evt = new AutoResetEvent(false);
    Timer t = new Timer(state => {
        // Do work
        evt.Set();
    }, null, 100, Timeout.Infinite);
    if (evt.WaitOne(500)) {
        // method called and completed
    } else {
        // timed out waiting
    }
}
2 голосов
/ 10 марта 2009

Так что именно вы тестируете? Вы тестируете, что таймеры работают? Или ваш код правильно настроил таймер, чтобы по истечении времени таймер выполнял обратный вызов? Не зная, как выглядит код, я предполагаю, что вы действительно хотите протестировать последнее. Мой ответ будет таким: (1) это, вероятно, будет трудно с помощью статических методов, и (2) вам, вероятно, придется использовать внедрение зависимостей, а также вводить таймеры ложных показаний и т. Д., Которые на самом деле не запускают полученный метод, а записывают через ожидания, что ваш код совершил правильные звонки.

1 голос
/ 07 ноября 2009

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

  1. Когда вы регистрируете выполнение синхронизированного делегата, устанавливается правильное время. Вероятно, это необходимо проверить с различными комбинациями таймаутов и количества делегатов.
  2. Делегат выполнен надлежащим образом.

Разделение тестов таким образом позволит вам проверить работоспособность вашего механизма синхронизации с небольшим количеством коротких тайм-аутов (тестирование всех случаев, которые вам необходимо рассмотреть). Помните, что вам, вероятно, понадобится немного свободы в реальном времени, которое требуется для выполнения данного делегата на основе текущей нагрузки в системе и сложности кода вашего компонента (то есть в IntervalManager). *

1 голос
/ 10 марта 2009

Да, выполнение, как в вашем примере, не будет работать.

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

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

Кстати, этот подход обычно называют насмешливый ; в этом случае тестовый класс будет фиктивным объектом.

0 голосов
/ 07 ноября 2009

Может быть, я что-то упускаю, но в модульном тестировании Visual Studio есть специальные атрибуты, которые вы можете добавить в методы для контроля порядка выполнения и других вещей. Это должно было быть автоматически сгенерировано, когда вы впервые выполнили проект модульного тестирования:

    #region Additional test attributes
    // 
    //You can use the following additional attributes as you write your tests:
    //
    //Use ClassInitialize to run code before running the first test in the class
    //[ClassInitialize]
    //public static void MyClassInitialize(TestContext testContext) {
    //}
    //
    //Use ClassCleanup to run code after all tests in a class have run
    //[ClassCleanup()]
    //public static void MyClassCleanup()
    //{
    //}
    //
    //Use TestInitialize to run code before running each test
    //[TestInitialize()]
    //public void MyTestInitialize()
    //{
    //}
    //
    //Use TestCleanup to run code after each test has run
    //[TestCleanup()]
    //public void MyTestCleanup()
    //{
    //}
    //
    #endregion

Таким образом, использование [ClassInitialize] должно позволить вам написать все, что должно сначала выполняться, в метод. Тогда ваши тесты могут быть запущены. Или вы можете использовать [TestInitialize] для запуска кода перед каждым тестом.

0 голосов
/ 10 марта 2009

Конечно, вы можете проверить это. Вам просто нужно дождаться его исполнения.

...