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

StackOverflow https://stackoverflow.com/questions/628751

Вопрос

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

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

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

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

Ваше здоровье,

Пол

Это было полезно?

Решение

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

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
    }
}

Другие советы

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

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

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

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

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

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

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

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

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

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

Возможно, я чего-то не понимаю, но модульное тестирование 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] для запуска кода перед каждым тестированием.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top