Юнит-тестирование именных лучших практик [закрыто]

564

Каковы лучшие практики для именования классов юнит-тестов и методов тестирования?

Это обсуждалось на SO ранее, в Каковы некоторые популярные соглашения об именах для модульных тестов?

Я не знаю, является ли это очень хорошим подходом, но в настоящее время в моих проектах тестирования у меня есть взаимно-однозначные сопоставления между каждым рабочим классом и классом тестирования, например, Productи ProductTest.

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

Джеймс Ньютон-Кинг
источник
См. Здесь: stackoverflow.com/questions/96297/…
Забытая точка с запятой
1
Это не ответ на ваш вопрос, но стоит прочитать: haacked.com/archive/2012/01/02/structuring-unit-tests.aspx
Robs
3
Руководство по стилю Google гласит: test<MethodUnderTest>_<state>например, testPop_emptyStack google-styleguide.googlecode.com/svn/trunk/javaguide.html 5.2.3 Названия методов. Если есть сомнения, следите за Google.
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
2
@CiroSantilli 轩 事件 法轮功 包 卓 轩 И в следующем предложении говорится: «Не существует единого правильного способа назвать методы испытаний». Пойди разберись.
user2418306
1
Я все еще предпочитаю рекомендацию Фила Хаака даже спустя годы.
pimbrouwers

Ответы:

524

Мне нравится стратегия именования Роя Ошерова , это следующее:

[UnitOfWork_StateUnderTest_ExpectedBehavior]

Он содержит всю необходимую информацию о названии метода и структурированно.

Единица работы может быть как один метод, класс или как несколько классов. Он должен представлять все вещи, которые должны быть проверены в этом тестовом примере и находятся под контролем.

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

[NameOfTheClassUnderTestTests]

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

Марк Климент
источник
228
Для меня нет смысла ставить имя метода в тестовом методе. Что если вы переименуете метод? Никакой инструмент рефакторинга не переименует тесты для Вас. В конечном итоге вы переименовываете методы тестирования вручную или, скорее всего, в тесты с неправильным названием. Это как с комментариями. Гораздо хуже, чем вообще не комментировать код.
Петр Перак
80
@ Пери, я думаю, что это компромисс. С одной стороны, названия ваших тестов могут устареть, с другой стороны, вы не можете сказать, какой метод тестирует ваш тест. Я считаю, что последний приходит гораздо чаще.
Джоэл МакБет
15
Чтобы добавить к комментарию Пери - все методы отвечают за некоторые действия, например UpdateManager.Update(). Имея это в виду, я склонен называть свои тесты WhenUpdating_State_Behaviourили WhenUpdating_Behaviour_State. Таким образом, я тестирую определенное действие класса, избегая при этом вставлять имя метода в имя теста. Но самое главное, я должен иметь представление о том, какая бизнес-логика терпит неудачу, когда я вижу название провального теста
Рамунас
8
Resharper и IntelliJ оба, вероятно, найдут ваш метод тестирования и предложат переименовать его для вас, если вы проведете рефакторинг / переименование с помощью этих инструментов. Также попробуйте заглянуть в комментарии, где вы упомянули имя метода и обновить их тоже.
Джефф Мартин
62
Хорошие имена методов часто совпадают с действиями, которые выполняет метод. Если вам нужно выбрать между именем теста после вашего метода или действием, которое метод выполняет, что может быть подсказкой, вам следует переименовать ваш метод . (Не в каждом случае, хотя)
Kaadzia
121

Мне нравится следовать стандарту именования «Должен» для тестов, при этом присваивая имя тестовому устройству после тестируемого модуля (т.е. класса).

Для иллюстрации (используя C # и NUnit):

[TestFixture]
public class BankAccountTests
{
  [Test]
  public void Should_Increase_Balance_When_Deposit_Is_Made()
  {
     var bankAccount = new BankAccount();
     bankAccount.Deposit(100);
     Assert.That(bankAccount.Balance, Is.EqualTo(100));
  }
}

Почему "должен" ?

Я считаю, что это вынуждает авторов тестов называть тест с предложением в виде строки «Должен [быть в каком-то состоянии] [после / до / когда] [действие имеет место]»

Да, написание «следует» везде становится немного повторяющимся, но, как я уже сказал, это заставляет писателей мыслить правильно (что может быть полезно для новичков). Плюс это обычно приводит к читаемому английскому имени теста.

Обновление :

Я заметил, что Джимми Богард также является поклонником «следует» и даже имеет библиотеку модульных тестов « Должен» .

Обновление (4 года спустя ...)

Для тех, кто заинтересован, мой подход к тестированию имен развивался годами. Одна из проблем с шаблоном « Должен быть» , который я описал выше, - это не просто понять, какой метод тестируется. Я думаю, что для ООП имеет смысл начинать имя теста с тестируемого метода. Для хорошо разработанного класса это должно привести к читаемым именам тестовых методов. Я сейчас использую формат, похожий на <method>_Should<expected>_When<condition>. Очевидно, что в зависимости от контекста вы можете заменить глаголы «Должен / Когда» на что-то более подходящее. Пример: Deposit_ShouldIncreaseBalance_WhenGivenPositiveValue()

Джек Уклея
источник
32
Может быть , даже лучше и менее многословны, просто написать предложение , которое говорит , что он делает, предполагая , что тестовые работы: increasesBalanceWhenDepositIsMade().
hotshot309
3
Недавно увидел статью, в которой упоминалось похожее соглашение об именах (жаль, что я не добавил его в закладки). Предназначен для того, чтобы сделать списки тестов очень удобочитаемыми при сортировке по тестовым приспособлениям. Вы видите что-то вроде «BankAccount», а затем под ним (в разных строках) «Should_Increase_Balance_When_Deposit_Is_Made» «Should_Decrease_Balance_When_Withdrawal_Is_Made» и т. Д. Очень похоже на спецификацию, которая является своего рода смыслом TDD.
Саймон Тьюси
Нашел статью. Это в блоге Джастина Этереджа CodeThinked. Начинаем издеваться над Moq 3 - часть 1 .
Саймон Тьюси
11
Я также использую Должен и Когда, но наоборот. например, WhenCustomerDoesNotExist_ShouldThrowException (). Для меня это имеет гораздо больший смысл, чем «Должен тогда» (т.е. в определенном сценарии должен быть определенный ожидаемый результат). Это также вписывается в AAA (Arrange, Act, Assert) ... Утверждение в конце ... не начало ;-)
bytedev
2
@Schneider: учитывая "следует" = "рекомендованный", а затем необязательный, мне интересно: не будет ли лексически лучше использовать "должен" = "должен" тогда обязательный / обязательный. Например, RFC делает разницу между ними. Таким образом, прохождение тестов рекомендуется или требуется?
черный волшебник
79

Мне нравится этот стиль именования:

OrdersShouldBeCreated();
OrdersWithNoProductsShouldFail();

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

Sklivvz
источник
63
но @ hotshot309, он может использовать .NET - Соглашения о капитализации .NET
Ace
2
@ Эй, я полностью согласен с тобой и заметил это примерно через минуту после того, как я разместил этот комментарий. Я поклялся, что удалил его, когда увидел свою ошибку, но почему-то, наверное, я этого не сделал. Прости за это.
hotshot309
3
@CoffeeAddict, потому что подчеркивания внутри идентификаторов - это <del> аберрация </ del>, которая не совсем идиоматична в C #
Sklivvz
2
Я также хотел бы избежать использования, shouldя буду предпочитать willтакOrdersWithNoProductsWillFail()
Калин
4
@Calin По моему мнению, использование Willне совсем уместно, и, делая это, вы на самом деле ошибочно говорите читателю, что тест ни в коем случае не провалится ... если вы используете, Willчтобы выразить что-то в будущем, что может не произойти, вы неправильное использование, тогда как Shouldэто лучший выбор, потому что это означает, что вы хотите / хотите, чтобы что-то произошло, но это не произошло или не могло, когда тест запускает, он сообщает вам, провалился ли он / успешно, поэтому вы не можете на самом деле подразумевать, что заранее, это логичное объяснение, что твое? почему вы избегаете Should?
Эяль Сольник
51

Кент Бек предлагает:

  • Один тестовый прибор на единицу (класс вашей программы). Испытательные приспособления - сами классы. Название испытательного прибора должно быть:

    [name of your 'unit']Tests
    
  • Тестовые случаи (методы тестового крепления) имеют такие имена, как:

    test[feature being tested]
    

Например, имея следующий класс:

class Person {
    int calculateAge() { ... }

    // other methods and properties
}

Тестовое приспособление будет:

class PersonTests {

    testAgeCalculationWithNoBirthDate() { ... }

    // or

    testCalculateAge() { ... }
}
Серхио Акоста
источник
4
Я хотел бы, чтобы больше людей следовали этим правилам. Не так давно мне пришлось переименовать более 20 методов тестирования, потому что они имели имена, такие как «ATest», «BasicTest» или «ErrorTest».
Клин
85
не префикс метода 'test' становится избыточным, учитывая суффикс класса?
Гэвин Миллер
50
Помните это, когда Кент написал эту книгу. Атрибуты не были изобретены. Поэтому имя Test в имени метода указывает на структуру теста, что метод был тестом. Также много произошло с 2002 года.
Томас Йесперсен
14
testCalculateAge ... это бессмысленное имя для вашего метода тестирования. «test» является избыточным (вы называете все ваши методы префиксом «method»?). У остальной части имени нет условий для проверки или того, что ожидалось. Является ли CalculateAge тестируемым методом? ..... кто знает ...
bytedev
1
Я хотел бы добавить, что при использовании этой стратегии в документации требуется указывать ожидаемый результат. В качестве примечания по поводу префикса 'test'; некоторые структуры модульного тестирования требуют определенных префиксов или суффиксов для распознавания тестов. Префикс абстрактных классов с помощью «Abstract» не считается избыточным (поскольку он самодокументируется), так почему же это не относится к «Test»?
siebz0r
17

Имена классов . Что касается имен тестовых приборов, я обнаружил, что «Тест» довольно распространен в вездесущем языке многих доменов. Например, в инженерной области: StressTestи в области косметики: SkinTest. Извините, что не согласен с Кентом, но использование «Test» в моих тестовых приспособлениях ( StressTestTest?) Сбивает с толку.

«Единица» также часто используется в доменах. Например MeasurementUnit. Класс называется MeasurementUnitTestтестом "Measurement" или "MeasurementUnit"?

Поэтому мне нравится использовать префикс "Qa" для всех моих тестовых классов. Например, QaSkinTestи QaMeasurementUnit. Его никогда не путают с объектами домена, и использование префикса, а не суффикса означает, что все тестовые приборы живут вместе визуально (полезно, если в вашем тестовом проекте есть подделки или другие вспомогательные классы)

Пространства имен . Я работаю в C # и держу свои тестовые классы в том же пространстве имен, что и класс, который они тестируют. Это удобнее, чем иметь отдельные тестовые пространства имен. Конечно, тестовые классы находятся в другом проекте.

Имена методов испытаний . Мне нравится называть мои методы WhenXXX_ExpectYYY. Это делает предусловие ясным и помогает с автоматизированной документацией (как TestDox). Это похоже на совет в блоге по тестированию Google, но с большим разделением предварительных условий и ожиданий. Например:

WhenDivisorIsNonZero_ExpectDivisionResult
WhenDivisorIsZero_ExpectError
WhenInventoryIsBelowOrderQty_ExpectBackOrder
WhenInventoryIsAboveOrderQty_ExpectReducedInventory
grundoon
источник
Вы говорили об именах тестовых методов и именах тестовых устройств. Имена тестовых приборов сопоставляются с производственными классами. где вы пишете название метода производства в вашем тесте?
Свет
12

Я использую понятие « дано, когда» . Взгляните на эту короткую статью http://cakebaker.42dh.com/2009/05/28/given-when-then/ . Статья описывает эту концепцию в терминах BDD, но вы можете использовать ее и в TDD без каких-либо изменений.

Pashec
источник
Given-When-Then - это то же самое, что MethodName_Scenario_ExpectedBehavior, не так ли ?!
Свет
1
Не совсем. Given_When_Then ссылается больше на: GivenAnEntity_WhenSomeActionHappens_ThatResultIsExpected Тест должен выразить желание протестировать поведение, а не реализацию .
plog17
1
«Дано, когда» часто называют корнишоном. Это DSL, который появился из инструментов Cucumber, JBehave и Behat.
bytedev
+1 это лучший метод ИМО. Разъединение того, как метод делает что-то из ожидаемого результата, является очень мощным и позволяет избежать множества проблем, описанных в других комментариях.
Ли
8

Смотрите: http://googletesting.blogspot.com/2007/02/tott-naming-unit-tests-responsbly.html

Для имен тестовых методов я лично нахожу использование подробных и самодокументированных имен очень полезным (наряду с комментариями Javadoc, которые дополнительно объясняют, что делает тест).

Атес Горал
источник
7

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

bytedev
источник
7

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

Допустим, я тестирую Settingsкласс в проекте в MyApp.Serializationпространстве имен.

Сначала я создам тестовый проект с MyApp.Serialization.Testsпространством имен.

В этом проекте и, конечно, в пространстве имен я создам класс с именем IfSettings(сохраненный как IfSettings.cs ).

Допустим, я тестирую SaveStrings()метод. -> Я назову тест CanSaveStrings().

Когда я запускаю этот тест, он показывает следующий заголовок:

MyApp.Serialization.Tests.IfSettings.CanSaveStrings

Я думаю, что это очень хорошо говорит мне, что это тестирование.

Конечно, полезно, чтобы в английском языке существительное «Тесты» было таким же, как и глагол «тесты».

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

Обычно имена тестов должны начинаться с глагола.

Примеры включают в себя:

  • Обнаруживает (например DetectsInvalidUserInput)
  • Броски (например ThrowsOnNotFound)
  • Будет (например WillCloseTheDatabaseAfterTheTransaction)

и т.п.

Другой вариант - использовать «это» вместо «если».

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

[ Изменить ]

После более длительного использования вышеуказанного соглашения об именах я обнаружил, что префикс If может сбивать с толку при работе с интерфейсами. Так уж получилось, что класс тестирования IfSerializer.cs очень похож на интерфейс ISerializer.cs в «вкладке« Открыть файлы »». Это может очень раздражать при переключении между тестами, тестируемым классом и его интерфейсом. В результате я бы сейчас выбрал That over If в качестве префикса.

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

[Test] public void detects_invalid_User_Input()

Я считаю, что это легче читать.

[ Конец редактирования ]

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

Торстен Лоренц
источник
2

В VS + NUnit я обычно создаю папки в своем проекте для группировки функциональных тестов вместе. Затем я создаю классы тестовых модулей и называю их по типу функциональности, которую я тестирую. Методы [Test] имеют следующие имена Can_add_user_to_domain:

- MyUnitTestProject   
  + FTPServerTests <- Folder
   + UserManagerTests <- Test Fixture Class
     - Can_add_user_to_domain  <- Test methods
     - Can_delete_user_from_domain
     - Can_reset_password
Кев
источник
2

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

Мне лично нравятся лучшие практики, описанные в "Карманном руководстве JUnit" ... трудно превзойти книгу, написанную соавтором JUnit!

Стив Мойер
источник
3
Не верьте, что это действительно отвечает на поставленный вопрос - не могли бы вы отредактировать и отредактировать JUnit Pocket Guide? Спасибо!
Нейт-Уилкинс
0

имя контрольного примера для класса Foo должно быть FooTestCase или что-то подобное (FooIntegrationTestCase или FooAcceptanceTestCase) - поскольку это контрольный пример. см. http://xunitpatterns.com/ для некоторых стандартных соглашений об именах, таких как test, test case, test fixture, test test и т. д.


источник