Структуры фальшивых каркасов против MS Fakes

Немного путают различия в структурах Mock, таких как NMock и VS 2011 Fakes Framework. Пройдя через MSDN, я понимаю, что Fakes позволяет вам издеваться над вашими зависимостями, как RhinoMock или NMock, однако подход отличается от других, Fakes генерирует код для достижения этой функциональности, но структура Mocks этого не делает. Так верно ли мое понимание? Является ли подделкой только другая структура Mock

Ответ 1

Ваш вопрос касался того, как структура MS Fakes отличается от NMock, и, похоже, другие ответы разрешили некоторые из них, но вот еще несколько сведений о том, как они совпадают и как они отличаются. NMock также похож на RhinoMocks и Moq, поэтому я группирую их с NMock.

Есть 3 основных различия, которые я вижу сразу между NMock/RhinoMocks/Moq и Framework Faces:

  • Структура фреймов MS использует сгенерированный код, как и Accessors в предыдущих версиях Visual Studio вместо общих типов. Если вы хотите использовать фальшивую фреймворк для зависимости, вы добавляете сборку, содержащую зависимость, к ссылкам тестового проекта, а затем щелкните ее правой кнопкой мыши, чтобы создать тестовые двойники (заглушки или прокладки). Затем, когда вы тестируете, вы фактически используете эти сгенерированные классы. NMock использует дженерики для достижения того же самого (т.е. IStudentRepository studentRepository = mocks.NewMock<IStudentRepository>()). На мой взгляд, рамочный подход MS Fakes запрещает навигацию и рефакторинг кода изнутри тестов, так как вы фактически работаете с сгенерированным классом, а не с вашим реальным интерфейсом.

  • Структура фальсификации MS снабжает окурки и кроты (прокладки), в то время как NMock, RhinoMocks и Moq все предоставляют заглушки и mocks. Я действительно не понимаю решение MS не включать в себя издевательства, и я лично не поклонник родинок по причинам, описанным ниже.

  • С фреймворком MS fakes вы предоставляете альтернативную реализацию методов, которые вы хотите заглушить. В рамках этих альтернативных реализаций вы можете указать возвращаемые значения и отслеживать информацию о том, как или был вызван метод. С помощью NMock, RhinoMocks и Moq вы создаете макет объекта, а затем используете этот объект, чтобы указать затушенные возвращаемые значения или отслеживать взаимодействия (как и как были вызваны методы). Я считаю подход подделок MS более сложным и менее выразительным.

Чтобы прояснить разницу в том, что обеспечивают фреймворки: NMock, RhinoMocks и Moq предоставляют два типа тестовых двойников (заглушки и макеты). Рамки подделок обеспечивают заглушки и кроты (они называют их прокладками) и, к сожалению, не включают в себя mocks. Чтобы понять различия и сходства между NMock и MS Fakes, полезно понять, каковы эти различные типы тестовых удвоений:

Stubs: Опоры используются, когда вам нужно предоставить значения для методов или свойств, которые будут заданы вашим тестовым удваиванием тестируемым методом. Например, когда мой тестируемый метод вызывает метод IsStudentExist() двойного тестового теста IStudentRepository, я хочу, чтобы он возвращал true.

Идея окурков в подделках NMock и MS одинакова, но с NMock вы бы сделали что-то вроде этого:

Stub.On(mockStudentRepository).Method("DoesStudentExist").Will(Return.Value(true));

И с помощью MSFakes вы сделаете что-то вроде этого:

IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() // Generated by Fakes.
{
    DoesStudentExistInt32 = (studentId) => { return new Student(); }
};

Обратите внимание, что в примере MS Fakes вы создаете совершенно новую реализацию для метода DoStudentExist (обратите внимание, что он называется DoesStudentExistInt32, потому что фальшивая инфраструктура добавляет типы данных параметров к именам методов, когда генерирует объекты-заглушки, я думаю, что это затеняет ясность тестов). Честно говоря, реализация NMock также меня пугает, потому что она использует строку для идентификации имени метода. (Простите меня, если я неправильно понял, как NMOCK предназначен для использования.) Этот подход действительно тормозит рефакторинг, и я настоятельно рекомендую RhinoMocks или Moq по NMock по этой причине.

Mocks: Mocks используются для проверки взаимодействия между тестируемым методом и его зависимостями. С NMock вы делаете это, устанавливая ожидания, подобные этому:

Expect.Once.On(mockStudentRepository).Method("Find").With(123);

Это еще одна причина, по которой я бы предпочел RhinoMocks и Moq над NMock, NMock использует старый стиль ожидания, тогда как RhinoMocks и Moq поддерживают подход Arrange/Act/Assert, где вы указываете ожидаемые взаимодействия в качестве утверждений в конце выполните следующие действия:

stubStudentRepository.AssertWasCalled( x => x.Find(123));

Снова отметим, что RhinoMocks использует лямбда вместо строки для идентификации метода. Рамка фреймов ms не предоставляет макетов вообще. Это означает, что в ваших завершенных реализациях (см. Описание заглушек выше) вы должны установить переменные, которые вы позже проверяли, были установлены правильно. Это будет выглядеть примерно так:

bool wasFindCalled = false;

IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() 
{
    DoesStudentExistInt32 = (studentId) => 
        { 
            wasFindCalled = true;
            return new Student(); 
        }
};

classUnderTest.MethodUnderTest();

Assert.IsTrue(wasFindCalled);

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

Моли (Шиммы): Чтобы быть откровенным, я не люблю родинок из-за их потенциального злоупотребления. Одна из вещей, которые мне так нравятся в отношении модульного тестирования (и, в частности, TDD), заключается в том, что тестирование кода помогает понять, где вы написали плохой код. Это связано с тем, что тестирование плохо написанного кода затруднено. Это неверно при использовании родинки, потому что молы на самом деле предназначены для того, чтобы вы могли тестировать зависимости, которые не были введены, или для проверки частных методов. Они работают аналогично окуркам, за исключением того, что вы используете ShimsContext следующим образом:

using (ShimsContext.Create())
{
    System.Fakes.ShimDateTime.NowGet = () => { return new DateTime(fixedYear, 1, 1); };
}

Мое беспокойство в отношении прокладок заключается в том, что люди начнут рассматривать их как "более простой способ unit test", потому что это не заставляет вас писать код так, как вам следует. Для более полной записи этой концепции см. Этот пост:

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

Если вы заинтересованы в изучении RhinoMocks здесь учебного видео Pluralsight (полное раскрытие информации - я написал этот курс и получаю роялти за просмотры, но я думаю, что это относится к этому обсуждению, поэтому я включаю его здесь):

Ответ 2

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

  • Ваша архитектура должна правильно использовать заглушки и инъекции зависимостей, а не полагаться на костыль подделок и макетов

  • Подделки и издевательства полезны для тестирования кода, который вы не должны или не можете изменить, например:

    • Устаревший код, который не использует (или эффективно использует) заглушки
    • сторонние API
    • Ресурсы, для которых у вас нет исходного кода

Шиммы (известный как "Moles", во время разработки) - это действительно насмешливая структура, которая работает путем обхода вызовов. Вместо того, чтобы кропотливо строить макет (да, даже используя Moq относительно болезненно!), Прокладки просто используют объект производственного кода уже на месте. Прокладки просто перенаправляют вызов из производственной цели в тестовый делегат.

Stubs генерируются из интерфейсов в целевом проекте. Объект Stub - это просто реализация интерфейса. Преимущество использования типа Stub заключается в том, что вы можете быстро генерировать заглушку, не загромождая свой тестовый проект многократными одноразовыми заглушками, не говоря уже о том, чтобы тратить время на их создание. Конечно, вы должны создать конкретные заглушки, чтобы использовать их во многих тестах.

Эффективная реализация подделок (прокладок, макетов и тисков) требует немного привыкания, но стоит того. Я лично сохранил недели времени разработки, используя типы Shims/Mole, Mocks и Stub. Надеюсь, у вас будет столько удовольствия от технологии, как у меня!

Ответ 3

Как я понимаю, команда Visual Studio хотела избежать конкуренции с различными макетными библиотеками, доступными для .NET. MS часто сталкивается с такими жесткими решениями. Они запружены, если они не обеспечивают определенную функциональность ( "почему MS не предоставляет нам макетную библиотеку, а это как-то обычное требование?" ) И прокляты, если они это сделают ( "почему Microsoft действует так агрессивно и водит ее естественных сторонников вне рынка?" ) Очень часто, но не всегда, они решают отступить от простого предоставления своей альтернативы имеющимся и хорошо принятым технологиям. Это похоже на это.

Элемент подкладки Fakes действительно очень полезен. Конечно, есть опасности. Требуется определенная дисциплина, чтобы обеспечить ее использование только там, где это необходимо. Однако он заполняет большой пробел. Моя основная жалоба заключается в том, что она поставляется только с выпуском Ultimate VS 2012 и поэтому будет доступна только подразделению разработчиков .NET. Какая жалость.

Ответ 4

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

Другая категория объектов, предложенная Fakes, называемая "прокладкой", предоставляет механизм для замены поведения зависимостей, которые не были (или не могут быть) надлежащим образом разделены для стандартной замены через mocks. AFAIK, TypeMock - единственная из основных смешных фреймворков, которая в настоящее время предлагает такую ​​функциональность.

Кстати, если вы уже опробовали Moles раньше, Fakes - это, по сути, одно и то же, наконец, выйдя из Microsoft Research и в реальный продукт.

Ответ 5

Что касается объектов Fake (Shim + Stub), он был хорошо определен выше, хотя, как я полагаю, последний абзац в последнем комментарии достаточно хорошо описывает всю ситуацию.

Хотя многие люди будут утверждать, что объекты Fake (Shim + Stub) являются хорошими активами в некоторых модульных тестах, недостатком является то, что независимо от того, используете ли вы Visual Studio 2012 или Visual Studio 2013, эти параметры ТОЛЬКО доступны с версиями Premium или Ultimate. IOW, это означает, что вы НЕ должны запускать ЛЮБОЙ из этих подделок (Shim + Stub) в любой версии Pro.

Вероятно, вы можете увидеть опцию Fakes (Shim + Stub) в версиях Pro, но будьте осторожны, есть некоторые довольно сильные шансы, что у вас не будет абсолютно ничего... Он не будет генерировать компиляционную ошибку, сообщающую вам что чего-то важного не хватает, вариантов просто нет, поэтому не тратьте свое время...

Это важный фактор, который следует учитывать в команде разработчиков, особенно если один из них использует версию Ultimate, в то время как все остальные используют версию Pro... С другой стороны, Moq можно легко установить через Nuget независимо от того, какая версия Visual Studio ты используешь. У меня не было проблем с использованием Moq, ключ с любым инструментом - знать, для чего они используются и как правильно их использовать;)