Стандарты кодирования модульных тестов

22

Обычно, когда мы говорим о стандартах кодирования, мы ссылаемся на код самой программы, но как насчет модульных тестов? Существуют ли определенные рекомендации по стандартам кодирования, которые являются уникальными для модульных тестов? Кто они такие?

EpsilonVector
источник

Ответы:

12

Вдобавок ко всему, я могу думать о трех различиях в стиле кодирования для тестового кода.

В именовании методов испытаний я следую схеме shouldDoSomethingWhenSomeConditionHolds.

Внутри теста принято следовать следующему интервалу:

@Test
shouldReturnAccountBalenceWhenGetBalenceIsCalled() {
    // Some lines 
    // of setup code
    // go here.

    // The action being tested happens after a blank line.

    // An assertion follows another blank line.
}

Некоторые настаивают только на одном утверждении на тест, но это далеко не универсально.

СУХОЙ (не повторяйся сам) менее важен в тестовом коде, чем в рабочем коде. Хотя некоторый повторяющийся код должен быть помещен в метод setUp или класс testUtils, стремление к нулевому повторению в тестовом коде приведет к тесно связанным и негибким тестам, что препятствует рефакторингу.

Эрик Уилсон
источник
Конечно, существует множество шаблонов, поэтому вы также должны дать ответ.
Эрик Уилсон,
10
Это шаблон Arrange, Act, Assert .
StuperUser
СУХОЙ по-прежнему важно. Если вам нужно сделать одни и те же утверждения в нескольких тестах, создайте общую функцию и вызовите ее во всех тестах.
MiFreidgeim SO-перестань быть злым
@MichaelFreidgeim Возможно, мы просто говорим о градусах, но у меня значительно выше допуск повторения в тестовом коде. У меня был опыт создания наборов тестов с очень небольшим повторением, и я обнаружил, что тесты стало сложно модифицировать и понимать при изменении требований. Затем я перестал беспокоиться о DRY в тестах, и мои тестовые наборы стали проще в использовании. <
Эрик Уилсон
16

Рой Ошеров рекомендует следующую схему именования ваших тестов:

NameOfMethodUnderTest_StateUnderTest_ExpectedBehavior() 

См. Http://weblogs.asp.net/rosherove/archive/2005/04/03/TestNamingStandards.aspx

Роберт Харви
источник
Я согласен с Роем. Это улучшает читаемость, хотя ReSharper постоянно говорит мне, что я должен удалить их NameOfMethodUnderTestStateUnderTestExpectedBehavior();)
Оскар Медерос
Как заставить это работать, когда метод перегружен, чтобы могло быть несколько методов с одинаковым именем?
Нарендра Патхай
6

Главное помнить, что юнит-тесты - это, по сути, мини-спецификации. Это означает, что акцент всегда должен делаться на удобочитаемости.

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

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

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

Я просто завершу с несколькими рекомендациями книги:

Тестовые шаблоны xUnit: рефакторинг тестового кода: отличная книга, некоторые говорят, что она немного сухая, но я так не думаю. Подробно рассказывается о множестве различных способов организации тестов и о том, как их поддерживать. Уместно, если вы используете что-то вроде NUnit и т. Д.

Искусство модульного тестирования: с примерами в .Net : Лучшая книга о том, как писать и поддерживать тесты. Несмотря на то, что я действительно новичок, я считаю, что разделы насмешек уже устарели, так как синтаксис AAA теперь довольно стандартный, а не просто другой способ сделать это.

Растущее объектно-ориентированное программное обеспечение, ориентированное на тесты : эта книга просто потрясающая! Безусловно, лучшая книга по модульному тестированию и единственная продвинутая, которая ставит модульное тестирование в качестве первоклассного гражданина в процессе проектирования. Читал это, когда это была публичная бета-версия и с тех пор рекомендую. Превосходный, реально работающий пример, используемый на протяжении всей книги. Рекомендую сначала прочитать книгу Роя.

FinnNk
источник
ИМХО нормально, чтобы модульные тесты содержали логику: вполне разумно тестировать высокооптимизированную и эффективную версию алгоритма, используя наивный алгоритм, который делает то же самое для определения правильного поведения. Например, представьте себе тестирование хеш-таблицы путем создания ассоциативного массива на основе линейного поиска.
дсимча
2
Да, но это относится за пределами теста в сборщиках тестовых данных (которые сами должны быть модульными, если логика внутри них не тривиальна). Исключением из этого могут быть сторонние библиотеки, которые, как правило, «доверяют», чтобы быть корректными и могут использоваться без тестов.
FinnNk
3

Не помещайте логику в свои юнит-тесты. Например, предположим, что вы тестируете метод add, у вас может быть что-то вроде этого:

void MyTest_SaysHello()
{
   string name = "Bob";
   string expected = string.Format("Hello, {0}", name);
   IMyObjectType myObject = new MyObjectType();
   string actual = myObject.SayHello(name);
   Assert.AreEqual(expected, actual);
}

В этом конкретном случае вы, скорее всего, повторяете ту же логику, что и в тесте, поэтому вы в основном тестируете «1 + 1 == 1 + 1», а не «1 + 1 == 2», то есть «настоящий» тест. Итак, что вы действительно хотите, чтобы ваш тестовый код был похож на это:

void MyTest_SaysHello()
{
   string expected = "Hello, Bob";
   IMyObjectType myObject = new MyObjectType();
   string actual = myObject.SayHello("Bob");
   Assert.AreEqual(expected, actual);
}
Хьюго
источник
2
Небольшое исправление: я думаю, что вы имели в виду «строка ожидается = строка. Формат (« Привет, Боб »)» должен быть «строка ожидается =« Привет, Боб »».
Майк Розенблюм
@MikeRosenblum вы, очевидно, правы, и кто-то пытался это исправить, но два рецензента отклонили это редактирование
Конрад Моравский
@ Конрад: это странно. Это форум по программированию, верно?
Майк Розенблюм
Я еще раз отредактировал ответ в соответствии с предложением Майка Розенблюма.
bdsl
0

Длинные, описательные имена методов. Помните, что методы тестирования никогда не вызываются из кода (они вызываются исполнителем модульных тестов, который обнаруживает и вызывает их посредством отражения), поэтому можно сходить с ума и иметь имена методов длиной 50-80 символов. Конкретное соглашение об именах (верблюд, нижнее подчеркивание, «должен», «должен», «когда», «дано» и т. Д.) Не очень важно, если имя отвечает на три вопроса:

  • что тестируется?
  • какие условия?
  • Каков ожидаемый результат?

Методы испытаний должны быть короткими .

Методы испытаний должны иметь простую линейную структуру . Нет, если или цикл конструкций.

Методы испытаний должны следовать шаблону "организовать-действовать-утверждать" .

Каждый тест должен проверять одну вещь . Обычно это означает одно утверждение за тест. Тест , как { Do A; Assert B; Assert C; }должны быть переработаны на два: { Do A; Assert B; }и{ Do A; Assert C; }

Избегайте случайных данных или таких вещей, как «DateTime.Now»

Убедитесь, что все элементы тестового прибора возвращаются в исходное состояние в конце теста (например, с использованием демонтажа )

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

azheglov
источник
-1

Что-то похожее на то, что Фармбой уже упоминал, формат имени моего метода

 <MethodName>Should<actionPerformed>When<Condition>

например

 GetBalanceShouldReturnAccountBalance() {
Амит Вадхва
источник