Какая разница между подделкой, издевательством и окурком?
Я знаю, как я использую эти термины, но мне интересно, есть ли общепринятые определения для подделки, насмешек и окурков для юнит-тестов? Как вы определяете их для ваших тестов? Опишите ситуации, когда вы можете использовать каждый из них.
Вот как я их использую:
Поддельный: класс, который реализует интерфейс, но содержит фиксированные данные и никакой логики. Просто возвращает "хорошие" или "плохие" данные в зависимости от реализации.
Mock: класс, который реализует интерфейс и позволяет динамически устанавливать значения, которые будут возвращаться / исключения генерировать из определенных методов, и предоставляет возможность проверить, были ли вызваны / не вызваны определенные методы.
Заглушка: как и фиктивный класс, за исключением того, что он не обеспечивает возможность проверки того, что методы были вызваны / не вызваны.
Макеты и заглушки могут быть сгенерированы вручную или сгенерированы при помощи насмешки Поддельные классы создаются вручную. Я использую mocks прежде всего для проверки взаимодействия между моим классом и зависимыми классами. Я использую заглушки, как только проверил взаимодействия и проверяю альтернативные пути в своем коде. Я использую поддельные классы в первую очередь для абстрагирования зависимостей от данных или когда макеты / заглушки слишком утомительны для настройки каждый раз.
17 ответов
Вы можете получить некоторую информацию:
От Мартина Фаулера о насмешках и заглушках
Поддельные объекты на самом деле имеют рабочие реализации, но обычно используют некоторые ярлыки, которые делают их непригодными для производства.
Заглушки обеспечивают постоянные ответы на вызовы, сделанные во время теста, обычно вообще не реагируя ни на что, кроме того, что запрограммировано для теста. Заглушки могут также записывать информацию о вызовах, такую как заглушка шлюза электронной почты, которая запоминает сообщения, которые она "отправила", или, возможно, только сколько сообщений она "отправила".
Мок - это то, о чем мы здесь говорим: объекты, предварительно запрограммированные с ожиданиями, которые формируют спецификацию вызовов, которые они ожидают получить.
Из http://xunitpatterns.com/:
Поддельный: мы приобретаем или создаем очень легкую реализацию той же функциональности, которая предоставляется компонентом, от которого зависит SUT, и поручаем SUT использовать его вместо реального.
Заглушка: эта реализация сконфигурирована для ответа на вызовы из SUT со значениями (или исключениями), которые будут использовать непроверенный код (см. Производственные ошибки на странице X) в SUT. Ключевым показателем для использования тестовой заглушки является наличие непроверенного кода, вызванного невозможностью контролировать косвенные входы SUT
Макет объекта, который реализует тот же интерфейс, что и объект, от которого зависит SUT (тестируемая система). Мы можем использовать Mock Object в качестве точки наблюдения, когда нам нужно выполнить Поведение Поведения, чтобы избежать наличия непроверенного требования (см. Ошибки при работе на стр. X), вызванного невозможностью наблюдать побочные эффекты от вызова методов на SUT.
Лично
Я пытаюсь упростить с помощью: Mock и Stub. Я использую Mock, когда это объект, который возвращает значение, установленное для тестируемого класса. Я использую Stub, чтобы имитировать интерфейс или абстрактный класс для тестирования. На самом деле, это не имеет значения, как вы это называете, это все классы, которые не используются в производстве и используются в качестве служебных классов для тестирования.
Заглушка - объект, который предоставляет предопределенные ответы на вызовы методов.
Макет - это объект, на который вы возлагаете ожидания.
Поддельный - объект с ограниченными возможностями (для целей тестирования), например, поддельный веб-сервис.
Test Double - общий термин для заглушек, издевательств и подделок. Но неофициально вы часто будете слышать, как люди называют их издевательствами.
Я удивлен, что этот вопрос существует так долго, и никто еще не дал ответ на основе "Искусства модульного тестирования" Роя Ошерова.
В "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". Вы найдете множество мнений.
Как отмечается в ответе, получившем наибольшее количество голосов, Мартин Фаулер обсуждает эти различия в Mocks, не являются заглушками, и, в частности, подзаголовок "Разница между Mocks и заглушками", поэтому обязательно прочитайте эту статью.
Вместо того, чтобы сосредоточиться на том, как эти вещи различны, я думаю, что более поучительно сосредоточиться на том, почему это разные понятия. Каждый существует для другой цели.
Подделки
Подделка - это реализация, которая ведет себя "естественно", но не является "реальной". Это нечеткие понятия, и поэтому разные люди по-разному понимают, что делает вещи поддельными.
Одним из примеров подделки является база данных в памяти (например, с использованием sqlite с :memory:
хранить). Вы никогда не будете использовать это для производства (так как данные не сохраняются), но это совершенно адекватно в качестве базы данных для использования в среде тестирования. Это также намного легче, чем "настоящая" база данных.
В качестве другого примера, возможно, вы используете какое-то хранилище объектов (например, Amazon S3) на производстве, но в тесте вы можете просто сохранять объекты в файлы на диске; тогда ваша реализация "сохранить на диск" будет подделкой. (Или вы можете даже подделать операцию "сохранить на диск", используя вместо этого файловую систему в памяти.)
В качестве третьего примера представьте объект, который предоставляет API кеша; объект, который реализует правильный интерфейс, но просто не выполняет никакого кэширования, но всегда возвращает промах кэша, будет своего рода подделкой.
Цель подделки не в том, чтобы повлиять на поведение тестируемой системы, а в том, чтобы упростить реализацию теста (удалив ненужные или тяжелые зависимости).
Столбики
Заглушка - это реализация, которая ведет себя "неестественно". Он предварительно сконфигурирован (обычно тестовой установкой) для ответа на конкретные входы с конкретными выходами.
Цель заглушки - привести тестируемую систему в определенное состояние. Например, если вы пишете тест для некоторого кода, который взаимодействует с REST API, вы могли бы заглушить REST API с помощью API, который всегда возвращает постоянный ответ или который отвечает на запрос API с определенной ошибкой. Таким образом, вы могли бы написать тесты, которые утверждают, как система реагирует на эти состояния; например, тестирование ответа, который получают ваши пользователи, если API возвращает ошибку 404.
Заглушка, как правило, реализуется, чтобы отвечать только на те взаимодействия, о которых вы сказали. Но ключевая особенность, которая делает что-то незавершенным, заключается в его назначении: заглушка - это все о настройке тестового примера.
Mocks
Макет похож на заглушку, но с добавленной проверкой. Цель макета состоит в том, чтобы сделать утверждения о том, как тестируемая система взаимодействовала с зависимостью.
Например, если вы пишете тест для системы, которая загружает файлы на веб-сайт, вы можете создать макет, который принимает файл и который можно использовать для подтверждения правильности загруженного файла. Или, в меньшем масштабе, обычно используют макет объекта для проверки того, что тестируемая система вызывает конкретные методы макетируемого объекта.
Насмешки связаны с интерактивным тестированием, которое является специфической методологией тестирования. Люди, которые предпочитают проверять состояние системы, а не системные взаимодействия, будут редко использовать макеты.
Тест удваивается
Подделки, заглушки и макеты все относятся к категории двойников. Двойной тест - это любой объект или система, которую вы используете в тесте, а не что-то еще. В большинстве случаев автоматизированное тестирование программного обеспечения предполагает использование тестовых двойников того или иного типа. Некоторые другие виды тестовых двойников включают фиктивные значения, шпионы и черные дыры ввода / вывода.
Вещь, которую вы утверждаете, называется фиктивным объектом, а все остальное, что только помогло выполнить тест, является заглушкой.
Все они называются Test Doubles и используются для внедрения зависимостей, которые нужны вашему тесту.
Заглушка: у него уже есть предопределенное поведение, чтобы установить ваше ожидание, например, заглушка возвращает только успешный случай вашего ответа API.
Мок - более умная заглушка. Вы подтверждаете, что ваш тест проходит через него. так что вы можете сделать макет, который возвращает либо успех, либо неудачу, в зависимости от условия, которое может быть изменено в вашем тестовом примере.
Unit testing
- это подход к тестированию, при котором модуль (класс, метод) находится под контролем.
Test double
- не первичный объект (из мира ООП). Это реализация, которая создается временно для тестирования, проверки или во время разработки. Типы тестовых пар:
fake object
представляет собой реальную реализацию интерфейса (протокола) или расширения, использующего наследование или другие подходы, которые можно использовать для создания -is
зависимость. Обычно он создается разработчиком как простейшее решение для замены какой-либо зависимостиstub object
- это простой объект (0, nil и методы без логики) с дополнительным состоянием, которое предопределено (разработчиком) для определения возвращаемых значений. Обычно создается фреймворкомmock object
очень похож наstub object
но дополнительное состояние изменяется во время выполнения программы, чтобы проверить, не произошло ли что-то (был вызван метод).spy object
это реальный объект с "частичным издевательством". Это означает, что вы работаете с не-двойным объектом, за исключением имитации поведения.dummy object
- это объект, который необходим для запуска теста, но ни одна переменная или метод этого объекта не вызывается.
Чтобы проиллюстрировать использование заглушек и макетов, я хотел бы также привести пример, основанный на " Искусстве модульного тестирования " Роя Ошерова.
Представьте себе, у нас есть приложение 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);
}
}
Если вы знакомы с Arrange-Act-Assert, то один из способов объяснить разницу между заглушкой и макетом, которая может оказаться полезной для вас, состоит в том, что заглушки принадлежат разделу упорядочения, так как они предназначены для упорядочения состояния ввода, а макеты раздел assert, как они для утверждения результатов против.
Манекены ничего не делают. Они просто для заполнения списков параметров, чтобы вы не получили неопределенных или нулевых ошибок. Они также существуют для удовлетворения проверки типов в строго типизированных языках, так что вам может быть разрешено компилировать и запускать.
Заглушки, фейки и макеты имеют разное значение в разных источниках. Предлагаю вам представить внутренние термины вашей команды и согласовать их значение.
Я думаю, что важно различать два подхода: - проверка поведения (подразумевает подстановку поведения) - проверка конечного состояния (подразумевает эмуляцию поведения)
Рассмотрите возможность отправки электронной почты в случае ошибки. При проверке поведения - вы проверяете этот методSend
из IEmailSender
был казнен один раз. И вам нужно эмулировать результат возврата этого метода, вернуть Id отправленного сообщения. Итак, вы говорите: "Я ожидаю, чтоSend
будет называться. И я просто верну фиктивный (или случайный) идентификатор для любого вызова ". Это проверка поведения:emailSender.Expect(es=>es.Send(anyThing)).Return((subject,body) => "dummyId")
При проверке состояния вам нужно будет создать TestEmailSender
что реализует IEmailSender
. И реализоватьSend
метод - путем сохранения ввода в некоторую структуру данных, которая будет использоваться для проверки будущего состояния, например, массив некоторых объектов SentEmails
а затем он проверяет, что вы проверите SentEmails
содержит ожидаемое электронное письмо. Это проверка состояния:Assert.AreEqual(1, emailSender.SentEmails.Count)
Из моих чтений я понял, что проверка поведения обычно называется фиксацией. А государственную проверку обычно называют заглушками или фейками.
Это вопрос экспресс-тестирования. Я устанавливаю ожидания на макете, если хочу, чтобы тест описывал отношения между двумя объектами. Я возвращаю значения, если настраиваю вспомогательный объект, чтобы получить интересное поведение в тесте.
Согласно книге «Принципы, практики и шаблоны модульного тестирования Владимира Хорикова»:
- Макеты: помогают эмулировать и исследовать исходящие взаимодействия. Эти взаимодействия представляют собой вызовы, которые ТУС делает своим зависимостям для изменения их состояния. Другими словами, это помогает изучить взаимодействие (поведение) ТРИ и ее зависимостей. издевательства могут быть:
- Шпион: создан вручную
- Моки: созданы с использованием фреймворка
- Заглушки: помогают эмулировать входящие взаимодействия. Эти взаимодействия представляют собой вызовы, которые ТУС делает своим зависимостям для получения входных данных. Другими словами, это помогает проверить данные, переданные в SUT. Это может быть 3 типа
- Fake: обычно реализуется для замены несуществующей зависимости.
- Dummy: жестко запрограммированное значение.
- Заглушки: расширенная зависимость, которую вы настраиваете для возврата разных значений для разных сценариев.
Заглушка и фальшивка являются объектами в том смысле, что они могут варьировать свой отклик в зависимости от входных параметров. Основное различие между ними заключается в том, что 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;
}
}
Из практического модульного тестирования с помощью JUnit и Mockito Томека Качановского.
Манекены и заглушки используются для подготовки среды к тестированию. Они не используются для проверки. Заглушка используется для передачи в качестве значения (например, как параметр прямого вызова метода), тогда как заглушка передает некоторые данные в SUT, заменяя один из ее DOC.
Целью тестовых шпионов и макетов является проверка правильности связи между SUT и DOC .
Фейк работает почти так же хорошо, как настоящий коллаборационист, но в чем-то проще и/или слабее (что делает его непригодным для промышленного использования). Кроме того, он обычно «дешевле» в использовании (т.е. быстрее или проще в настройке), что делает его пригодным для тестов (которые должны выполняться как можно быстрее). Типичным примером является база данных в памяти, которая используется вместо полноценного сервера базы данных. Его можно использовать для некоторых тестов, поскольку он довольно хорошо обслуживает SQL-запросы; однако вы не захотите использовать его в производственной среде. В тестах фейк играет аналогичную роль с фиктивным и заглушкой : он является частью окружения (тестового приспособления), а не объектом проверки. Подделки используются в интеграционных тестах , а не в модульных тестах.
В книге « Шаблоны тестов xUnit » Джерарда Месароса есть хорошая диаграмма, которая дает хорошее представление о различиях.
Я многому научился на следующем ресурсе с превосходным объяснением Роберта К. Мартина (дядя Боб):
Маленький пересмешник в блоге "Чистый код"
Это объясняет различия и тонкости
- манекены
- двойной тест
- заглушки
- шпионы
- (правда) издевается
- подделками
В нем также упоминается Мартин Фаулер, и он объясняет немного истории тестирования программного обеспечения.
Я ни в коем случае не собираюсь отвечать на этот вопрос по этой ссылке как за верный ответ. Тем не менее, это помогло мне лучше понять концепции насмешек и шпионажа, поэтому я отвечаю на это, надеясь, что это поможет большему количеству людей.
Я обычно использую только 2 термина — Fake и Mock .
Имитация только при использовании фиктивной среды, такой как Moq, например, потому что кажется неправильным называть ее подделкой, когда она создается с помощьюnew Mock<ISomething>()
- хотя технически вы можете использовать фиктивный фреймворк для создания заглушек или подделок , просто кажется глупым называть его так в данной ситуации - это должен быть макет .
Подделка для всего остального. Если Фейк можно охарактеризовать как реализацию с урезанными возможностями , то я думаю, что Заглушка тоже может быть Фейком (а если нет, то какая разница, все понимают, о чем я, и еще ни разу никто не сказал «Я думаю, вы найди, что это заглушка" )