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


564

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

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

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

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



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();

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


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

Вы говорили об именах тестовых методов и именах тестовых устройств. Имена тестовых приборов сопоставляются с производственными классами. где вы пишете название метода производства в вашем тесте?
— Свет

12

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


Given-When-Then - это то же самое, что MethodName_Scenario_ExpectedBehavior, не так ли ?!
— Свет

1
Не совсем. Given_When_Then ссылается больше на: GivenAnEntity_WhenSomeActionHappens_ThatResultIsExpected Тест должен выразить желание протестировать поведение, а не реализацию .
— plog17

1
«Дано, когда» часто называют корнишоном. Это DSL, который появился из инструментов Cucumber, JBehave и Behat.
— bytedev

+1 это лучший метод ИМО. Разъединение того, как метод делает что-то из ожидаемого результата, является очень мощным и позволяет избежать множества проблем, описанных в других комментариях.
— Ли


7

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


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 и т. д.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.