Должен ли я изменить соглашение об именовании для моих модульных тестов?

StackOverflow https://stackoverflow.com/questions/1018525

Вопрос

В настоящее время я использую простое соглашение для своих модульных тестов.Если у меня есть класс с именем "EmployeeReader", я создаю тестовый класс с именем "EmployeeReader.Тесты.Затем я создаю все тесты для класса в тестовом классе с такими именами, как:

  • Reading_Valid_Employee_Data_Correctly_Generates_Employee_Object Считывание_значения_employee_object
  • Исключение Reading_Missing_Employee_Data_Throws_Invalid_Employee_ID_Exception

и так далее.

Недавно я читал о другой тип соглашения об именовании используется в BDD.Мне нравится удобочитаемость этого наименования, в итоге получается список тестов, что-то вроде:

  • When_Reading_Valid_Employee (фиксированный параметр)
    • Employee_Object_Is_Generated (метод) - объект, созданный сотрудником
    • Employee_Has_Correct_ID (метод) Employee_Has_Correct_ID
  • When_Reading_Missing_Employee (фиксированный параметр)
    • значение_invalid_employee_id_exception_is_thrown (метод)

и так далее.

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

Это было полезно?

Решение

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

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

Другие советы

Соглашение об именовании, которое я использую, является:

Название функции должно быть таким, когда возникает такая ситуация

Например, это могут быть некоторые тестовые имена для функции 'pop' стека

Исключение pop_shouldthrowemptystackexception_ whenthestackisempty

Pop_shouldreturn Объект поверх стека, когда появляется объект Поверх стека

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

Часть рассуждений, лежащих во втором соглашении об именовании, на которое вы ссылаетесь, заключается в том, что вы создаете тесты и поведенческие спецификации одновременно.Вы устанавливаете контекст, в котором все происходит, и что на самом деле должно произойти в этом контексте.(По моему опыту, наблюдения / методы тестирования часто начинаются с "should_", поэтому вы получаете стандартный формат "When_the_invoicing_system_is_told_to_email_the_client", "should_initiate_connection_to_mail_server".)

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

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

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

По сути, тестовые классы - это контексты, которые могут быть вложенными, а все методы - это однострочные утверждения.Например,

public class MyClassSpecification
{
    protected MyClass instance = new MyClass();

    public class When_foobar_is_42 : MyClassSpecification 
    {
        public When_foobar_is_42() {
            this.instance.SetFoobar( 42 ); 
        }

        public class GetAnswer : When_foobar_is_42
        {
            private Int32 result;

            public GetAnswer() {
                this.result = this.GetAnswer();
            }

            public void should_return_42() {
                Assert.AreEqual( 42, result );
            }
        }
    }
}

который даст мне следующий результат в моем тестовом раннере:

MyClassSpecification+When_foobar_is_42+GetAnswer
    should_return_42

Я прошел по двум путям, которые вы описали в своем вопросе, а также по нескольким другим...Ваш первый вариант довольно прост и понятен большинству людей.Лично мне больше нравится стиль BDD (ваш второй пример), потому что он изолирует различные контексты и группирует наблюдения в этих контекстах.Единственным реальным недостатком является то, что он генерирует больше кода, поэтому начинать делать это кажется немного более громоздким, пока вы не увидите аккуратные тесты.Кроме того, если вы используете наследование для повторного использования настройки прибора, вам нужен testrunner, который выводит цепочку наследования.Рассмотрим класс "An_empty_stack", и вы хотите повторно использовать его, чтобы затем создать другой класс:"When_five_is_pushed_on :An_empty_stack" вы хотите, чтобы это было как вывод, а не просто "When_five_is_pushed_on".Если ваш testrunner не поддерживает это, ваши тесты будут содержать избыточную информацию, такую как:"When_five_is_pushed_ On_empty_stack Когда_выходной_стакан :An_empty_stack" просто для того, чтобы сделать вывод приятным.

я голосую за вызов класса test case:EmployeeReaderTestCase и вызов методов(), подобных http://xunitpatterns.com/Organization.html и http://xunitpatterns.com/Organization.html#Test%20Naming%20Conventions

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top