Какая разница между подделкой, издевательством и окурком?


706

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

Вот как я их использую:

Поддельный : класс, который реализует интерфейс, но содержит фиксированные данные и никакой логики. Просто возвращает «хорошие» или «плохие» данные в зависимости от реализации.

Mock : класс, который реализует интерфейс и дает возможность динамически устанавливать значения, которые возвращаются / исключения генерируются из определенных методов, и предоставляет возможность проверить, были ли вызваны / не вызваны определенные методы.

Заглушка : как и фиктивный класс, за исключением того, что он не обеспечивает возможность проверки того, что методы были вызваны / не вызваны.

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


6
Ну, вы в основном сказали все это в своем «вопросе» :) Я думаю, что это довольно хорошо принятые определения этих терминов
Эран Гальперин

2
Определение Fake в Википедии отличается от этого, утверждая, что Fake "используется как более простая реализация, например, использует базу данных в памяти в тестах вместо реального доступа к базе данных)" См. En.wikipedia.org/wiki/Test_double
zumalifeguard

2
Я многому научился на следующем ресурсе с превосходным объяснением Роберта К. Мартина (дядя Боб): Маленький издеватель в блоге «Чистый код» . Это объясняет различия и тонкости манекенов, тестовых двойников, пней, шпионов, (истинных) издевательств и подделок. В нем также упоминается Мартин Фаулер, и он объясняет немного истории тестирования программного обеспечения.
Эрик

testing.googleblog.com/2013/07/… (краткая одностраничная сводка).
ShreevatsaR

Вот мое мнение, чтобы объяснить, что: Test Doubles: Fakes, Stubs и Mocks (сообщение в блоге с примерами)
michal-lipski

Ответы:


548

Вы можете получить некоторую информацию:

От Мартина Фаулера о насмешках и заглушках

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

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

Mocks - это то, о чем мы здесь говорим: объекты, предварительно запрограммированные с ожиданиями, которые формируют спецификацию вызовов, которые они ожидают получить.

Из xunitpattern :

Поддельный : мы приобретаем или строим очень легкую реализацию той же функциональности, которая предоставляется компонентом, от которого зависит SUT, и поручаем SUT использовать его вместо реального.

Заглушка : эта реализация настроена для ответа на вызовы из SUT со значениями (или исключениями), которые будут использовать непроверенный код (см. «Производственные ошибки» на странице X) в рамках SUT. Ключевым показателем для использования тестовой заглушки является наличие непроверенного кода, вызванного невозможностью контролировать косвенные входы SUT

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

Лично

Я пытаюсь упростить с помощью: Mock и Stub. Я использую Mock, когда это объект, который возвращает значение, установленное для тестируемого класса. Я использую Stub для имитации интерфейса или абстрактного класса для тестирования. На самом деле, не имеет значения, как вы это называете, это все классы, которые не используются в производстве и используются в качестве служебных классов для тестирования.


9
Мне кажется, что определения для Stub и Fake поменялись местами в цитате xUnitPattern по сравнению с цитатой Мартина Фаулера. Кроме того, определения Мартина Фаулера Stub и Fake обращены вспять по сравнению с определениями в первоначальном вопросе tvanfosson. В действительности существуют ли общепринятые определения этих двух терминов, или это просто зависит от того, с кем вы разговариваете?
Саймон Тьюси

3
+1 за «Я пытаюсь упростить с помощью: макет и заглушка». Это отличная идея!
Брэд Купит

4
Не понимаю, как использовать только Mock и Stub - отличная идея. Каждый тестовый дубль имеет свои цели и, следовательно, свое использование.
Гектор Ордонез

1
Я не вижу разницы между Фейком и Мок в определении MF.
IdontCareAboutReputationPoints

2
@MusuNaji: В определении MF нет никаких «ожиданий» в отношении разговора для подделки, кроме того, что он имеет реализацию для своего интерфейса. С другой стороны, Mock будет подвергнут сомнению (этот метод назывался?).
дбалакирев

205

Заглушка - объект, который предоставляет предопределенные ответы на вызовы методов.

Макет - это объект, на который вы возлагаете ожидания.

Поддельный - объект с ограниченными возможностями (для целей тестирования), например, поддельный веб-сервис.

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


4
Может ли кто-нибудь объяснить и определить мне, что такое «законсервированный ответ» в этом контексте?
MasterMastic

14
Явное значение, а не значение, которое рассчитывается.
Майк

В заключение! Некоторые определения, которые я могу понять! Исходя из этих определений, googletest (gtest) / googlemock (gmock) также позволяет заглушки из поддельных объектов, так как вы можете создавать EXPECT_CALL()s для смоделированного метода, который форсирует определенные выходные данные на основе определенных входных данных, используя тип .WillOnce(Invoke(my_func_or_lambda_func))(или with .WillRepeatedly()) синтаксис, прикрепленный к EXPECT_CALL(). Некоторые примеры использования Invoke()можно увидеть в другом контексте в нижней части моего длинного ответа здесь: stackoverflow.com/a/60905880/4561887 .
Габриэль Стейплс

Документация по Gmock Invoke()находится здесь: github.com/google/googletest/blob/master/googlemock/docs/… . В любом случае, вывод таков : макет Google (gmock) позволяет легко создавать и макеты, и заглушки , хотя большинство макетов не являются заглушками.
Габриэль Стейплс

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

94

Я удивлен, что этот вопрос существует так долго, и никто пока не дал ответ на основе основе «Искусства модульного тестирования» Роя Ошерова .

В «3.1 Введение заглушек» заглушка определяется как:

Заглушка - это управляемая замена существующей зависимости (или сотрудника) в системе. Используя заглушку, вы можете протестировать свой код, не обращаясь непосредственно к зависимости.

И определяет разницу между заглушками и макетами как:

Главное, что нужно помнить при использовании mocks против заглушек, это то, что mocks подобны заглушкам, но вы утверждаете против фиктивного объекта, тогда как вы не утверждаете против заглушки.

Подделка - это просто имя, используемое как для заглушек, так и для издевательств. Например, когда вас не волнует различие между заглушками и издевательствами.

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

  • Когда ваш тест проверяет значения в тестируемом классе или где-то еще, кроме подделки, подделка использовалась в качестве заглушки. Он просто предоставлял значения для использования тестируемым классом, либо напрямую через значения, возвращаемые вызовами к нему, либо косвенно, вызывая побочные эффекты (в некотором состоянии) в результате вызовов к нему.
  • Когда ваш тест проверяет значения подделки, он использовался как макет.

Пример теста, в котором класс FakeX используется в качестве заглушки:

const pleaseReturn5 = 5;
var fake = new FakeX(pleaseReturn5);
var cut = new ClassUnderTest(fake);

cut.SquareIt;

Assert.AreEqual(25, cut.SomeProperty);

fakeЭкземпляр используется в качестве заглушки , так как Assertне используют fakeвообще.

Пример теста, где тестовый класс X используется в качестве макета:

const pleaseReturn5 = 5;
var fake = new FakeX(pleaseReturn5);
var cut = new ClassUnderTest(fake);

cut.SquareIt;

Assert.AreEqual(25, fake.SomeProperty);

В этом случае Assertпроверяет значение наfake , делая это притворным.

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

Я согласен с тем, что Ошерове

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

Утверждение против фальшивки - это то, чего вы действительно хотите избежать, так как ваши тесты сильно зависят от реализации класса, который вообще не является тестируемым. Это означает, что тесты для класса ActualClassUnderTestмогут начать ломаться, потому что реализация для ClassUsedAsMockизменилась. И это создает неприятный запах для меня. Тесты на тестирование ActualClassUnderTestдолжны быть предпочтительно только тогда, когдаActualClassUnderTest изменении.

Я понимаю, что написание утверждений против подделки - обычная практика, особенно когда вы подписчик TDD типа «издеватель». Я полагаю, что я твердо нахожусь с Мартином Фаулером в лагере классицистов (см. Мартина Фаулера «Насмешки - не заглушки» ) и, подобно Ошерову, стараюсь избегать тестирования взаимодействия (которое может быть сделано только путем утверждения против подделки).

Чтобы получить удовольствие от прочтения того, почему вы должны избегать насмешек, как здесь определено, поищите в Google "fowler mockist classicist". Вы найдете множество мнений.


31

Как отмечается в ответе, получившем наибольшее количество голосов, Мартин Фаулер обсуждает эти различия в Mocks, не являются заглушками , и, в частности, подзаголовок «Разница между Mocks и заглушками» , поэтому обязательно прочитайте эту статью.

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

Подделки

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

Одним из примеров подделки является база данных в памяти (например, использование sqlite с :memory:хранилищем). Вы никогда не будете использовать это для производства (так как данные не сохраняются), но это совершенно адекватно в качестве базы данных для использования в среде тестирования. Это также намного легче, чем «настоящая» база данных.

В качестве другого примера, возможно, вы используете какое-то хранилище объектов (например, Amazon S3) в производстве, но в тесте вы можете просто сохранять объекты в файлы на диске; тогда ваша реализация "сохранить на диск" будет подделкой. (Или вы можете даже подделать операцию «сохранить на диск», используя вместо этого файловую систему в памяти.)

В качестве третьего примера представьте объект, который предоставляет API кеша; объект, который реализует правильный интерфейс, но просто не выполняет никакого кэширования, но всегда возвращает промах кэша, было бы своего рода подделкой.

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

Столбики

Заглушка является реализация , которая ведет себя «неестественно». Он предварительно сконфигурирован (обычно тестовой установкой) для ответа на конкретные входы с конкретными выходами.

Цель заглушки - привести тестируемую систему в определенное состояние. Например, если вы пишете тест для некоторого кода, который взаимодействует с REST API, вы могли бы заглушить REST API с помощью API, который всегда возвращает постоянный ответ или который отвечает на запрос API с определенной ошибкой. Таким образом, вы могли бы написать тесты, которые утверждают, как система реагирует на эти состояния; например, тестирование ответа, который получают ваши пользователи, если API возвращает ошибку 404.

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

Mocks

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

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

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

Тест удваивается

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


11

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

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

Вот логика, которую мы хотели бы проверить внутри LogAnalyzer:

if(fileName.Length<8)
{
 try
  {
    service.LogError("Filename too short:" + fileName);
  }
 catch (Exception e)
  {
    email.SendEmail("a","subject",e.Message);
  }
}

Как проверить, что LogAnalyzer правильно вызывает службу электронной почты, когда веб-служба выдает исключение? Вот вопросы, с которыми мы сталкиваемся:

  • Как мы можем заменить веб-сервис?

  • Как мы можем смоделировать исключение из веб-службы, чтобы мы могли проверить звонок в службу электронной почты?

  • Как мы узнаем, что служба электронной почты была вызвана правильно или вообще?

Мы можем решить первые два вопроса, используя заглушку для веб-службы . Чтобы решить третью проблему, мы можем использовать фиктивный объект для службы электронной почты .

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

[TestFixture]
public class LogAnalyzer2Tests
{
[Test]
 public void Analyze_WebServiceThrows_SendsEmail()
 {
   StubService stubService = new StubService();
   stubService.ToThrow= new Exception("fake exception");
   MockEmailService mockEmail = new MockEmailService();

   LogAnalyzer2 log = new LogAnalyzer2();
   log.Service = stubService
   log.Email=mockEmail;
   string tooShortFileName="abc.ext";
   log.Analyze(tooShortFileName);

   Assert.AreEqual("a",mockEmail.To); //MOCKING USED
   Assert.AreEqual("fake exception",mockEmail.Body); //MOCKING USED
   Assert.AreEqual("subject",mockEmail.Subject);
 }
}

9

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


1
в то время как другие ответы имеют большие детали и действительно хороши. это делает это настолько ясным и легким, чтобы понять разницу, трудно не высказаться. GJ!
Марио Гарсия

6

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


6

Если вы знакомы с Arrange-Act-Assert, то один из способов объяснить разницу между заглушкой и макетом, которая может оказаться полезной для вас, состоит в том, что заглушки принадлежат разделу упорядочения, так как они предназначены для упорядочения входного состояния, а макеты принадлежат раздел assert, как они для утверждения результатов против.

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


3

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

Я думаю, что важно различать два подхода: - проверка поведения (подразумевает подстановку поведения) - проверка конечного состояния (подразумевает эмуляцию поведения)

Рассмотрим отправку электронной почты в случае ошибки. При выполнении проверки поведения - вы убедитесь , что метод Sendиз IEmailSenderказнили один раз. И вам нужно эмулировать возвращаемый результат этого метода, вернуть идентификатор отправленного сообщения. Итак, вы говорите: «Я ожидаю, что Sendбудет вызван. И я просто верну фиктивный (или случайный) Id для любого вызова» . Это проверка поведения: emailSender.Expect(es=>es.Send(anyThing)).Return((subject,body) => "dummyId")

При проверке состояния вам нужно будет создать TestEmailSenderэти инструменты IEmailSender. И реализуйте Sendметод - сохраняя входные данные в некоторой структуре данных, которая будет использоваться для будущей проверки состояния, например, массив некоторых объектов, SentEmailsи затем он проверяет, что вы проверите, который SentEmailsсодержит ожидаемую электронную почту. Это проверка состояния: Assert.AreEqual(1, emailSender.SentEmails.Count)

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


Действительно хорошо детализированное и четкое определение.
Шьям Сундар Сингх Томар

2

stub и fake являются объектами в том смысле , что они могут варьировать свой отклик в зависимости от входных параметров. Основное различие между ними заключается в том, что Fake ближе к реальной реализации, чем заглушка. Заглушки содержат в основном жестко запрограммированные ответы на ожидаемый запрос. Давайте посмотрим на пример:

public class MyUnitTest {

 @Test
 public void testConcatenate() {
  StubDependency stubDependency = new StubDependency();
  int result = stubDependency.toNumber("one", "two");
  assertEquals("onetwo", result);
 }
}

public class StubDependency() {
 public int toNumber(string param) {
  if (param == “one”) {
   return 1;
  }
  if (param == “two”) {
   return 2;
  }
 }
}

Макет является шагом вверх от подделок и пней. Макеты предоставляют ту же функциональность, что и заглушки, но более сложные. Для них могут быть определены правила, определяющие, в каком порядке должны вызываться методы их API. Большинство mocks может отслеживать, сколько раз был вызван метод и может реагировать на основании этой информации. Обычно издевательства знают контекст каждого вызова и могут по-разному реагировать в разных ситуациях. Из-за этого издевательства требуют некоторых знаний о классе, над которым они издеваются. заглушка обычно не может отследить, сколько раз был вызван метод или в каком порядке была вызвана последовательность методов. Макет выглядит так:

public class MockADependency {

 private int ShouldCallTwice;
 private boolean ShouldCallAtEnd;
 private boolean ShouldCallFirst;

 public int StringToInteger(String s) {
  if (s == "abc") {
   return 1;
  }
  if (s == "xyz") {
   return 2;
  }
  return 0;
 }

 public void ShouldCallFirst() {
  if ((ShouldCallTwice > 0) || ShouldCallAtEnd)
   throw new AssertionException("ShouldCallFirst not first thod called");
  ShouldCallFirst = true;
 }

 public int ShouldCallTwice(string s) {
  if (!ShouldCallFirst)
   throw new AssertionException("ShouldCallTwice called before ShouldCallFirst");
  if (ShouldCallAtEnd)
   throw new AssertionException("ShouldCallTwice called after ShouldCallAtEnd");
  if (ShouldCallTwice >= 2)
   throw new AssertionException("ShouldCallTwice called more than twice");
  ShouldCallTwice++;
  return StringToInteger(s);
 }

 public void ShouldCallAtEnd() {
  if (!ShouldCallFirst)
   throw new AssertionException("ShouldCallAtEnd called before ShouldCallFirst");
  if (ShouldCallTwice != 2) throw new AssertionException("ShouldCallTwice not called twice");
  ShouldCallAtEnd = true;
 }

}

1

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

stub objectявляется голым объектом (0, nil и методы без логики) с дополнительным и предопределенным (разработчиком) состоянием для определения возвращаемых значений. Обычно это создается фреймворком

mock objectочень похоже на, stub objectно дополнительное состояние изменяется во время выполнения программы, чтобы проверить, произошло ли что-то (вызван метод).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.