Гораздо важнее тестировать только одну концепцию в каждом модульном тесте.
Для проверки концепции может потребоваться несколько утверждений, поэтому не беспокойтесь слишком о количестве утверждений. Конечно, если вы получите большое количество утверждений, вы можете сделать шаг назад и подумать о том, что вы на самом деле тестируете.
В подобных ситуациях, если я пытаюсь быть строгим в отношении одного утверждения для каждого теста, я буду утверждать равенство для Foo, а не для его компоненты. Это побуждает меня писать Foo.equals (), и это обычно хорошо само по себе. Но я не всегда строго придерживаюсь одного утверждения на тест. Посмотрите, что вам нравится.
В тесте часто используется несколько утверждений, но я не считаю, что это «лучшая практика».
Я бы сказал, что да, вы действительно хотите использовать несколько тестов для указанной выше функции, именно поэтому вы можете видеть, в чем проблема. Недавно я работал с довольно большим набором тестов, в котором есть некоторые странные сбои, которые мне еще предстоит отследить, и проблема, с которой я сталкиваюсь, заключается в том, что каждый из тестовых примеров делает слишком много. Я разделил их, и теперь я могу отключить определенные, которые не работают, чтобы я мог вернуться к ним, когда у меня будет возможность.
Тем не менее, вы все равно можете использовать приспособление, чтобы исключить общность вашей настройки и разборки. Я не могу говорить с MSTest, но в UnitTest ++ вы бы сделали что-то вроде:
class FooFixture
{
public:
FooFixture() : f(MakeFoo(kX, kY, kZ)) { }
static const int kX = 1;
static const int kY = 2;
static const int kZ = 3;
Foo f;
};
TEST_FIXTURE(FooFixture, IsXInitializedCorrectly)
{
CHECK_EQUAL(kX, f.X);
}
// repeat for Y and Z
Это не намного больше набора текста, особенно с учетом вырезания и вставки. Черт возьми, в vim это просто esc y ctrl- {pp
, а затем небольшие правки. Однако с такой настройкой вы можете увидеть, не работает ли только одно поле, а затем покопаться, чтобы понять, почему.
Обычно я использую несколько утверждений в методе, но обычно все они связаны с текущим тестом. Иногда я бросаю assert, чтобы проверить предварительное условие, которое, как я знаю, нарушит тест, потому что я не хочу, чтобы тест завершился случайно.
[Test] // NUnit style test.
public void SearchTypeAndInventory()
{
var specification = new WidgetSearchSpecification
{
WidgetType = Widget.Screw,
MinimumInventory = 10
};
var widgets = WidgetRepository.GetWidgets(specification);
if( !widgets.Any() )
Assert.Inconclusive("No widgets were returned.");
Assert.IsTrue(
widgets.All( o => o.WidgetType == Widget.Screw),
"Not all returned widgets had correct type");
Assert.IsTrue(
widgets.All( o => o.InventoryCount >= 10),
"Not all returned widgets had correct inventory count.");
* Хотя я мог бы объединить утверждения, я считаю более полезным разделить то, что пошло не так.
Я не думаю, что соблюдение жесткого правила «одно утверждение на тест» очень полезно. Что еще более важно, тест проверяет только одну вещь. Я видел много сверхтестов, которые представляют собой один огромный метод, который проверяет все в классе. Эти сверхтесты хрупкие и их сложно поддерживать.
Вы должны думать, что ваши тесты так же важны и хорошо написаны, как и код, который они тестируют. Например. применяйте те же методы рефакторинга, чтобы убедиться, что тест делает одно и делает это хорошо, а не является дымоходом, который проверяет все, что находится в поле зрения.
Как я читаю книги, вы должны делать «Красный, зеленый, рефакторинг». В части «Рефакторинг» вы должны провести рефакторинг как тестируемого кода, так и модульных тестов.
Я не вижу ничего плохого в следующем рефакторинге:
[TestMethod]
public void TestOne()
{
LetsDoSomeSetup();
AssertSomething();
}
[TestMethod]
public void TestTwo()
{
LetsDoSomeSetup(); // Same setup
AssertSomethingElse();
}
[TestMethod]
public void TestOneTwo()
{
LetsDoSomeSetup();
AssertSomething();
AssertSomethingElse();
}
Конечно, это предполагает, что два утверждения связаны, и, конечно, основывается на одном и том же сценарии.
AFAI видите, принцип, лежащий в основе практики
дело не в том, что вам нужно одно утверждение 'NUNit' / 'xUnit' для каждого теста; скорее у вас есть одно логическое утверждение для каждого теста.
[Test]
public void MakeFoo_SeedsInstanceWithSpecifiedAttributes()
{
Assert.AreEqual( new Foo(1,2,3), Foo.MakeFoo(1,2,3), "Objects should have been equal );
}
Здесь у меня есть одно утверждение NUnit (удовлетворяющее полиции утверждений): Однако он тестирует три вещи за кулисами (в Foo.Equals я бы проверял, равны ли все переменные).
Рекомендация состоит в том, чтобы предотвратить такие тесты (т. Е. Интеграционный тест, маскирующийся под модульный тест)
[Test]
public void ITestEverything()
{
// humongous setup - 15 lines
payroll.ReleaseSalaries();
// assert that the finance department has received a notification
// assert employee received an email
// assert ledgers have been updated
// statements have been queued to the printqueue
// etc.. etc..
}
Мораль истории: Это хорошая цель, к которой стоит стремиться. Попробуйте собрать все вещи, которые вы хотите протестировать, в одно логическое / связное утверждение с хорошим названием. например Assert.True (AreFooObjectsEqual (f1, f2)
. Если вы обнаружите, что вам сложно назвать связную проверку / утверждение - возможно, вам нужно пересмотреть свой тест и посмотреть, нужно ли его разделить .
На самом деле я написал дополнение для NUnit, чтобы помочь с этим. Попробуйте его на http://www.rauchy.net/oapt