Мок-фреймворк против фреймворков MS Fakes

99

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

Найруз Нилафдин
источник

Ответы:

189

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

Я сразу вижу 3 основных различия между NMock / RhinoMocks / Moq и MS Fakes Framework:

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

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

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

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

Заглушки: заглушки используются, когда вам нужно предоставить значения для методов или свойств, которые будут запрашиваться у ваших тестовых двойников тестируемым методом. Например, когда мой тестируемый метод вызывает метод DoesStudentExist () тестового двойника 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 вы создаете совершенно новую реализацию для метода DoesStudentExist (обратите внимание, что он называется DoesStudentExistInt32, потому что фреймворк фейков добавляет типы данных параметров к именам методов, когда он генерирует объекты-заглушки, я думаю, что это затемняет ясность тесты). Честно говоря, реализация NMock также вызывает у меня ошибки, потому что она использует строку для идентификации имени метода. (Простите меня, если я неправильно понял, как предполагается использовать NMock.) Этот подход действительно препятствует рефакторингу, и по этой причине я настоятельно рекомендую RhinoMocks или Moq вместо NMock.

Моки: Моки используются для проверки взаимодействия между тестируемым методом и его зависимостями. С 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 fakes вообще не предоставляет имеков. Это означает, что в ваших зарезервированных реализациях (см. Описание заглушек выше) вы должны установить переменные, которые, как вы позже убедитесь, были установлены правильно. Это будет выглядеть примерно так:

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); };
}

Меня беспокоит то, что люди начнут рассматривать их как «более простой способ модульного тестирования», потому что они не заставляют вас писать код так, как вы должны. Для более полного описания этой концепции см. Этот мой пост:

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

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

Джим Купер
источник
14
@Jim Я не согласен с тем, что позволять одному «тестировать зависимости, которые не внедряются» - это плохо. Напротив, на практике эта возможность помогает избежать загромождения кодовой базы множеством бессмысленных интерфейсов и связанной с ними конфигурации / сложности «объектного связывания». Я уже видел несколько проектов (как Java, так и .NET), которые имели сотни интерфейсов Java / C # только с одним реализующим классом и множеством бесполезных конфигураций XML (для компонентов Spring DI или в Web.config для MS Unity фреймворк). Такие зависимости лучше не вводить.
Rogério
19
@Rogerio, я работал над несколькими проектами с тысячами классов, которые следуют этой модели, и когда внедрение зависимостей сделано правильно (если вы используете конфигурацию XML, вы делаете это неправильно, imho), это просто и относительно безболезненно. С другой стороны, я работал над системами, которые этого не делают, и связь между классами всегда причиняла мне значительную боль. Тестирование не является причиной использования внедрения зависимостей (хотя это и неплохая причина). Настоящая причина - ослабленная муфта. Наличие интерфейсов, реализуемых одним классом, никогда не причиняло мне боли.
Джим Купер
17
@ Джим, я считаю возможность тестировать плохой дизайн большим плюсом. Первое, что вы хотите сделать перед рефакторингом устаревшего кода в сторону лучшего дизайна, - это написать тесты.
Thomas Materna
4
Мое правило: я использую Fakes только тогда, когда мне нужно использовать Shim для общего / статического метода или класса, к которому у меня нет доступа для реализации интерфейса. Для всего остального я использую Moq. Подделки работают медленнее (поскольку необходимо создавать поддельные сборки), и требуется больше усилий по кодированию, чтобы соответствовать функциональности, которую вы получаете с помощью Moq.
Ник
5
@ CarloV.Dango Во-первых, я прекрасно знаю, что внедрение зависимостей не требует отдельных интерфейсов; мой предыдущий комментарий просто намекал на тенденцию создавать такие интерфейсы при использовании DI. Во-вторых, «IOC» (Inversion of Control) не имеет ничего общего с DI! (Первый касается инверсии потока управления между методами, а второй касается разрешения зависимостей для компонента / объекта.) Путать IOC и DI, это вы показываете невежество, а не я; и, честно говоря, этот сайт не заслуживает того, чтобы люди оскорбляли других, поэтому, пожалуйста, оставьте его вежливым.
Rogério
23

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

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

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

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

Прокладки (известные как «Родинки» во время разработки) действительно представляют собой фиктивную структуру, которая работает посредством обходных вызовов. Вместо кропотливого создания имита (да, даже использование Moq относительно болезненно!), Прокладки просто используют уже существующий производственный объект кода. Прокладки просто перенаправляют вызов от производственной цели к делегату тестирования.

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

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

Майк Кристиан
источник
11
Проголосовали против из-за отсутствия конкретики и некоторых проблем с терминологией в ваших комментариях к фейкам. Подделки - это общий термин для всех типов тестовых двойников, а также название MS, используемое для их библиотеки подделок. В их библиотеке только прокладки на самом деле являются кротами, заглушки - нет. Что касается необходимости в примерах, я хотел бы увидеть в вашем ответе пример, который показывает, как создание прокладки (или заглушки) с помощью Fakes проще, чем использование Moq. Если вы внесете изменения в свой ответ, чтобы решить эту проблему, я изменю свой голос против.
Джим Купер,
1
Но добавляет хорошее обоснование для использования прокладок (молей), т.е. стороннего кода, системных / SDK API. Дело не только в том, что вы работаете со своими собственными решениями. Так что я дам вам один голос, чтобы сравнять это :-)
Тони Уолл
2
@ Майк и Джим ... Я постарался исправить терминологию, использованную в этом ответе. Пожалуйста, дайте мне знать, сможем ли мы это исправить. Спасибо.
Snesh
15

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

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

Чарльз Янг
источник
2
Фреймворк фейков также доступен в версии Premium.
AlwaysAProgrammer
13

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

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

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

Николь Калиною
источник
1
Обновление: теперь Moles интегрирован в MS Fakes. «Фреймворк Fakes в Visual Studio 2012 - это следующее поколение Moles & Stubs. Fakes отличается от Moles, поэтому переход от Moles к Fakes потребует некоторых изменений в вашем коде. Фреймворк Moles не будет поддерживаться в Visual Studio 2012 . " Источник: research.microsoft.com/en-us/projects/moles
Сир
1

Что касается фальшивых (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, ключ к любому инструменту - знать, для чего они используются и как их использовать правильно;)

SirXamelot
источник
1
Разделите вопрос на более мелкие абзацы, чтобы его было легче читать.
@SirXamelot, без рефакторинга кода вы не можете изменить вывод .net при условии статических вызовов, например DateTime.Now или Guid.NewGuid
zaitsman