Какая разница между подделкой, издевательством и окурком? - PullRequest
606 голосов
/ 06 декабря 2008

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

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

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

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

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

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

Ответы [ 10 ]

478 голосов
/ 06 декабря 2008

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

С Мартин Фаулер о Насмешке и Стабе

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

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

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

С xunitpattern :

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

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

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

Лично

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

178 голосов
/ 03 марта 2011

Stub - объект, предоставляющий предопределенные ответы на вызовы методов.

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

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

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

86 голосов
/ 25 октября 2015

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

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

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

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

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

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

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

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

Пример теста, в котором класс 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, что делает фальсификацию.

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

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

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

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

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

Для забавного прочтения о том, почему вы должны избегать насмешек, как это определено здесь, Google для "классицист Фаулер mockist". Вы найдете множество мнений.

7 голосов
/ 06 апреля 2017

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

Представьте себе, у нас есть приложение 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);
 }
}
5 голосов
/ 06 марта 2019

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

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

Подделки

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

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

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

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

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

Заглушки

A stub - это реализация, которая ведет себя "неестественно". Он предварительно сконфигурирован (обычно тестовой установкой) для ответа на конкретные входы с конкретными выходами.

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

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

* * Mocks тысячи сорок-девять

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

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

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

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

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

5 голосов
/ 21 мая 2009

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

3 голосов
/ 29 марта 2017

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

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

2 голосов
/ 21 июля 2018

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

1 голос
/ 08 марта 2019

Я многому научился на следующем ресурсе с превосходным объяснением Роберта К. Мартина (дядя Боб):

Маленький издеватель в блоге «Чистый код»

Объясняет различия и тонкости

  • манекены
  • тест удваивается
  • Заглушка
  • Шпионы
  • (правда) издевается
  • подделки

В нем также упоминается Мартин Фаулер и объясняется небольшая история тестирования программного обеспечения.

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

0 голосов
/ 13 ноября 2018

заглушка и подделка являются объектами в том смысле, что они могут варьировать свой отклик в зависимости от входных параметров. основное различие между ними заключается в том, что 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;
  }
 }
}

A mock - шаг вперед от подделок и пней. Макеты предоставляют ту же функциональность, что и заглушки, но более сложные. Для них могут быть определены правила, определяющие, в каком порядке должны вызываться методы их 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;
 }

}
...