Вопрос

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

Какой метод лучше всего использовать при модульном тестировании?

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

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

Решение

Как гласит мантра: «Используйте самое простое, что может сработать».

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

Избегайте использования макетов всегда потому что они делают тесты хрупкими.Ваши тесты теперь имеют подробные сведения о методах, вызываемых реализацией, если имитируемый интерфейс или ваша реализация изменятся...твои тесты ломаются.Это плохо, потому что вы потратите дополнительное время на запуск тестов вместо того, чтобы просто запустить SUT. Тесты не должны быть слишком тесно связаны с реализацией.
Так что используйте свое лучшее суждение..Я предпочитаю макеты, когда это поможет мне не писать обновление поддельного класса с помощью n>>3 методов.

Обновлять Эпилог/Обсуждение:
(Спасибо Торану Биллапсу за пример мокаст-теста.См. ниже)
Привет, Дуг! Я думаю, что мы перешли к еще одной священной войне — классические TDD-игры против Mockist TDD-игры.Я думаю, что я принадлежу к первому.

  • Если я прохожу тест № 101 Test_ExportProductList и обнаруживаю, что мне нужно добавить новый параметр в IProductService.GetProducts().Я делаю это, получаю этот тест зеленым.Я использую инструмент рефакторинга для обновления всех остальных ссылок.Теперь я считаю, что все мокистские тесты, называющие этого участника, провалились.Потом мне придется вернуться и обновить все эти тесты — пустая трата времени.Почему произошел сбой в методе MustPopulateProductsListOnViewLoadWhenPostBackIsFalse?Это потому, что код сломан?Скорее тесты сломаны.Я предпочитаю один провал теста = 1 место для исправления.Частота насмешек противоречит этому.Заглушки были бы лучше?Если бы у меня был fake_class.GetProducts()..Конечно, одно место, где можно переодеться, вместо операции с дробовиком из-за нескольких вызовов Expect.В конце концов это вопрос стиля..если бы у вас был общий служебный метод MockHelper.SetupExpectForGetProducts() - этого тоже было бы достаточно.но вы увидите, что это редкость.
  • Если поместить белую полоску на название теста, тест будет трудно прочитать.Множество сантехнического кода для макетной платформы скрывают фактическое выполнение теста.
  • требует, чтобы вы изучили этот конкретный вариант насмешливой структуры

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

Обычно я предпочитаю использовать макеты из-за ожиданий.Когда вы вызываете метод заглушки, который возвращает значение, он обычно просто возвращает вам значение.Но когда вы вызываете метод в макете, он не только возвращает значение, но также обеспечивает установленное вами ожидание того, что метод вообще был вызван.Другими словами, если вы устанавливаете ожидание, а затем не вызываете этот метод, генерируется исключение.Когда вы устанавливаете ожидание, вы по сути говорите: «Если этот метод не вызван, что -то пошло не так». И наоборот, правда, если вы назовите метод на макете и не установили ожидания, это сделает исключение, по сути, сказав: «Эй, что вы делаете, называете этот метод, когда вы этого не ожидали».

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

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

И на это...

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

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

Лучше всего использовать комбинацию, и вам придется принять собственное решение.Вот рекомендации, которые я использую:

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

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

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

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

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

<TestMethod()> _
Public Sub Should_Populate_Products_List_OnViewLoad_When_PostBack_Is_False()
    mMockery = New MockRepository()
    mView = DirectCast(mMockery.Stub(Of IProductView)(), IProductView)
    mProductService = DirectCast(mMockery.DynamicMock(Of IProductService)(), IProductService)
    mPresenter = New ProductPresenter(mView, mProductService)
    Dim ProductList As New List(Of Product)()
    ProductList.Add(New Product())
    Using mMockery.Record()
        SetupResult.For(mView.PageIsPostBack).Return(False)
        Expect.Call(mProductService.GetProducts()).Return(ProductList).Repeat.Once()
    End Using
    Using mMockery.Playback()
        mPresenter.OnViewLoad()
    End Using
    'Verify that we hit the service dependency during the method when postback is false
    Assert.AreEqual(1, mView.Products.Count)
    mMockery.VerifyAll()
End Sub

Не говоря уже о Statist vs.Взаимодействие.Подумайте о ролях и отношениях.Если объект сотрудничает с соседом для выполнения своей работы, то эта связь (выраженная в интерфейсе) является кандидатом на тестирование с использованием макетов.Если объект представляет собой простой объект-значение с небольшим поведением, протестируйте его напрямую.Я не вижу смысла писать моки (или даже заглушки) вручную.Вот как мы все начали и отреагировали на это.

Для более длительного обсуждения рассмотрите возможность взглянуть на http://www.mockobjects.com/book

Прочитайте обсуждение Люком Кейнисом именно этого вопроса в этот пост в блоге.Он ссылается сообщение от Джея Филдса что даже предполагает, что использование [эквивалента Ruby/mocha] stub_everything предпочтительнее, чтобы сделать тесты более надежными.Процитируем последние слова Филдса:«Mocha позволяет определить макет так же легко, как и заглушку, но это не значит, что вы всегда должны отдавать предпочтение макетам.На самом деле, я обычно предпочитаю заглушки и при необходимости использую макеты».

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