Для меня дело в том, что я исключил основной класс из компиляции. Этот ответ был более чем полезен для меня.
Ваш второй пример (имеющий приспособление для каждой логической "задачи", а не по одному для каждого класса) имеет то преимущество, что вы можете иметь различную логику SetUp и TearDown для каждой задачи, тем самым упрощая вашу отдельные методы тестирования и сделать их более удобочитаемыми.
Вам не нужно останавливаться на одном или другом в качестве стандарта. Мы используем сочетание того и другого, в зависимости от того, сколько различных «задач» мы должны протестировать для каждого класса.
Я использовал следующее соглашение об именах:
functionName_shouldDoThis_whenThisIsTheSituation
Например, это будут некоторые тестовые имена для функции «pop» стека
pop_shouldThrowEmptyStackException_whenTheStackIsEmpty
pop_shouldReturnTheObjectOnTheTopOfTheStack_whenThereIsAnObjectOnTheStack
Я считаю, что второй вариант лучше, потому что он делает ваши модульные тесты более удобочитаемыми для других, поскольку длинные строки делают код более трудным для чтения или затрудняют просмотр. Если вы все еще чувствуете некоторую двусмысленность относительно того, что делает тест, вы можете добавить комментарии, чтобы прояснить это.
Часть аргументов в пользу второго соглашения об именах, на которое вы ссылаетесь, состоит в том, что вы создаете тесты и поведенческие спецификации одновременно. Вы устанавливаете контекст, в котором что-то происходит, и что на самом деле должно происходить в этом контексте. (По моему опыту, наблюдения / методы тестирования часто начинаются с «should_», поэтому вы получаете стандартный формат «When_the_invoicing_system_is_told_to_email_the_client», «should_initiate_connection_to_mail_server».)
Существуют инструменты, которые отражаются на ваших тестовых приспособлениях и выводят красиво форматированный html spec sheet, без подчеркивания. В итоге вы получаете удобочитаемую документацию, синхронизированную с реальным кодом (если вы сохраняете высокий и точный охват тестами).
Я голосую за вызов класса тестового примера: EmployeeReaderTestCase и за вызов методов (), например http://xunitpatterns.com/Organization.html и http: //xunitpatterns.com/Organization.html#Test%20Naming%20Conventions[1245 visible
Я использую второй метод, и он действительно помогает в описании того, что должно делать ваше программное обеспечение. Я также использую вложенные классы для более подробного описания контекста.
По сути, тестовые классы - это контексты, которые могут быть вложенными, а методы - это все однострочные утверждения. Например,
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 (ваш второй пример), потому что он изолирует разные контексты и группирует наблюдения в этих контекстах. Единственным реальным недостатком является то, что он генерирует больше кода, поэтому начинать его делать немного сложнее, пока вы не увидите аккуратные тесты. Также, если вы используете наследование для повторного использования настройки фикстуры, вам нужен тестер, который выводит цепочку наследования. Рассмотрим класс «An_empty_stack», и вы хотите использовать его повторно, чтобы затем выполнить другой класс: «When_five_is_pushing_on: An_empty_stack», который вы хотите получить в качестве вывода, а не просто «When_five_is_pasted_on».