Разве это дурной тон-рассчитывать на порядок выполнения юнит-тестов NUnit


Я создавал модульные тесты как сумасшедший и обнаружил, что мне часто приходится настраивать что-то в одном тесте, что я только что сорвал в предыдущем тесте. Есть ли смысл создавать что-то (например, запись базы данных) в одном тесте (например, тест вставки), а затем использовать его для последующего теста (например, тест удаления)? Или же каждое испытание должно всегда проходить само по себе?

Можете ли вы даже определить порядок тестов в NUnit или они всегда выполняются в алфавитном порядке?

Примечание: Я специально спрашиваю о порядке тестов внутри одного тестового файла. Не через тестовые файлы или каким-либо образом более глобально.

Update: Спасибо всем, кто ответил - было Много хороших ответов, и смысл группы довольно единодушен. Я выбрал ответ Джона Нолана, поскольку он дал наиболее полное объяснение и множество ссылок. Как вы, наверное, догадались, у меня было сильное искушение нарушить это Рул, несмотря на то, что думал, что это может быть немного "вонючим", как выразился Джон. Спасибо также Fortyrunner за добавление тега unit-testing .

8   15   2009-01-31 02:33:31

8 ответов:

Опора на порядок ваших тестов указывает на то, что вы сохраняете состояние во всех тестах. Это и есть вонючий

Более чистый способ тестирования-это когда вы зависите только от одного элемента функциональности, поведение которого хотите проверить. Обычно Вы издеваетесь над другими объектами, которые вам нужны, чтобы заставить ваш метод функционировать. Хороший способ думать о приближении к модульным тестам-это шаблонорганизовать, действовать, утверждать .

Ниже приводится фрагмент из отличной бесплатной книги Карла Сегина . Я annoted организовать, закон и отстаивать.

[TestFixture] public class CarTest 
{ 
    [Test] public void SaveCarCallsUpdateWhenAlreadyExistingCar()   
    {
         //Arrange
         MockRepository mocks = new MockRepository();
         IDataAccess dataAccess = mocks.CreateMock<IDataAccess>();   
         ObjectFactory.InjectStub(typeof(IDataAccess), dataAccess); 
         //Act
         Car car = new Car(); 
         Expect.Call(dataAccess.Save(car)).Return(389); 
         mocks.ReplayAll(); 
         car.Save(); 
         mocks.VerifyAll(); 
         // Assert
         Assert.AreEqual(389, car.Id); 
         ObjectFactory.ResetDefaults();
    } 
}

Посмотрите в Настройки тестового приспособления, которые позволяют указать функции, которые будут выполняться перед любым из тестов в приспособлении. Это позволяет выполнить общую настройку один раз, и она будет выполняться всегда, независимо от того, выполняется ли один тест или все тесты в наборе.

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

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

Я бы рассматривал каждый тест как полностью независимый от любого другого теста. Даже если бы вы могли предписать порядок тестов, это был бы кошмар обслуживания, когда тесты должны измениться.

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

Я настоятельно рекомендую сделать все ваши модульные тесты независимыми.

Ваша бизнес-логика / структура базы данных и т. д. может измениться со временем, так что вам в конечном итоге придется заменить или переписать (или даже отказаться) существующие модульные тесты - и если у вас есть несколько других тестов в зависимости от того, который вы заменяете, это может вызвать ненужные проблемы, потому что вам придется пройти все остальные тесты, а также проверить, работают ли они по-прежнему, как ожидалось.

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

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

Если у вас есть тесты с отслеживанием состояния (обычная проблема с работой базы данных - что я делаю, когда я не на SO), то мне кажется, что избегать порядка в тестовом файле не совсем необходимо. Тем не менее, вы должны признать, что если у вас есть 2 теста, с тестом 2 в зависимости от прохождения теста 1, то вы получите "катастрофический" двойной сбой, если тест 1 не пройдет, потому что тест 2 не имеет ожидаемой установки (и, более того, вы хотите беспокоиться, если тест 2 действительно пройдет после того, как тест 1 не прошел, если вы не пройдете тест 1). думайте, что тест 2 зависит от прохождения теста 1).

Вот почему вы хотите, чтобы тесты были независимыми, когда это возможно - как внутри файла, так и между файлами.

Было бы очень неразумно зависеть от порядка между (наборами) тестов в разных файлах.