Создание и запуск тестов - PullRequest
       7

Создание и запуск тестов

3 голосов
/ 17 февраля 2012

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

Очевидно, что тесты должны быть надежными, то есть тест должен давать сбой, только если модуль не работает должным образом, а не из-за плохо построенного теста. Итак, первый вопрос: Каким руководствам следует следовать для построения теста?

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

// This is not a test method, but a support method for the test methods.
private void AddSomeElements(ICollection c, int count)
{
    Random rand = new Random();
    ...
    for(int i=0; i < count; i++)
        c.Add(...);
}

Так, например, тест для свойства Count может быть:

/// <summary>
///A test for Count
///</summary>
[TestMethod()]
public void CountTest()
{
    HostsCache target = new HostsCache();
    AddSomeElements(target, 100);
    int actual;
    actual = target.Count;
    Assert.AreEqual<int>(100, actual);
 }

Правильный ли этот подход? Предположим, что метод Add возвращает значение (например, значение bool): в этом случае частный метод должен также возвращать это значение?

Ответы [ 5 ]

5 голосов
/ 17 февраля 2012

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

Вторая самая большая ошибка, которую я видел, - это слишком большое количество кода в каждом тесте. Каждый тест, в идеале, должен тестировать только несколько строк кода. Определенно не больше кода, чем содержится в одном методе.

Итак, если в вашем примере AddSomeElements имеет смысл что-то возвращать, пусть он вернет что-то. Если нет, не надо.

Просто запомни две вещи:

  1. Ваши тесты имеют первоклассный код
  2. Каждый тест должен проверять только очень маленький кусочек функциональности.
3 голосов
/ 17 февраля 2012

Правило 1: не пытайтесь кипятить океан.Тестируйте только небольшой фрагмент кода за раз.Для модульных тестов постарайтесь минимизировать внешние зависимости и взаимодействия.Это включает в себя не тратить время на проверку работоспособности основной операционной системы и, например, базовые классы, предоставляемые .NET Framework.Хорошо проверить, правильно ли ваш код взаимодействует с этими внешними вещами, но имейте в виду, что вы сосредоточены на тестировании написанного вами кода, а не на тестировании кода любого другого, который использует ваш код.

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

Если вы пишете свою собственную реализацию стандартного интерфейса ICollection, то вам следует выполнить модульное тестирование реализации вашей коллекции.В противном случае вам не нужно проверять, что вызов ICollection.Add () в классе коллекции акций, предоставляемом .NET, делает то, что должен.

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

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

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

Одним из примеров такого внутреннего хука доступа является атрибут InternalsVisibleTo в .NET.Вы заявляете в своей производственной сборке, что ваша тестовая сборка может получить доступ к членам класса, объявленным с "внутренней" видимостью.То, что вы можете автоматически объявить закрытым, может быть добавлено к внутреннему, чтобы сделать его видимым для ваших модульных тестов.Данные по-прежнему защищены от обычных клиентов.

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

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

3 голосов
/ 17 февраля 2012

Это довольно открытый вопрос, но вот несколько мнений, которые помогут вам в этом.

Каждый тест обычно состоит из трех частей.

a) Arrange - установите значения для отправки в метод, который вы собираетесь тестировать, создавая проверяемую и воспроизводимую среду.
б) Действуй - назови свой метод или еще какой-нибудь тест
c) Утверждение - убедитесь, что ваш метод сделал то, что должен, то есть вернул правильное значение или произвел правильный побочный эффект.

В качестве вкуса я положил три комментария, разграничивающих эти разделы // Arrange // Act // Assert

Организация может иногда быть сложной, если ваш метод не настроен на «тестируемость». Начните изучать информацию о внедрении зависимостей. Как только вы сделаете свои вещи тестируемыми, появятся такие инструменты, как Moq или Typemock или Rhino Mocks , которые могут помочь.

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

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

3 голосов
/ 17 февраля 2012

Вы должны выполнить несколько простых тестов по добавлению и удалению элементов.

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

Логическое возвращаемое значение должно проверяться отдельно.

например, 1) создать коллекцию и добавить элемент, а затем утверждать, что коллекция содержит один элемент. 2) создайте коллекцию и добавьте элемент, утверждая, что элемент, который вы вставили, - это тот же элемент, который вы получили 3) если метод add возвращает bool, то проверка сценариев для Add, возвращающего true, и Add, возвращающего false, должна быть еще двумя тестовыми примерами.

2 голосов
/ 17 февраля 2012

Первоначально я неправильно понял ваш вопрос, мои извинения.

Совершенно разумно создать класс или проект TestHelpers, который будет содержать повторно используемый код, чтобы помочь вам написать свои тесты.Например, если вы работаете с наборами тестовых данных, вы можете создать несколько методов для получения GetSampleTestDataForScenarioX, где "ScenarioX" - это описательное имя для вашего сценария.

В общем, модульные тесты должнысодержат очень мало на пути разветвления или зацикливания.Я следую модели тестирования Arrange / Act / Assert, где вы начинаете с организации теста (создание объектов, создание условий теста и т. Д.), Затем воздействуете на данные теста, а затем утверждаете, что мои условия выполняются.

...