Я читал различные статьи о насмешках против окурков в тестировании, в том числе «Мокки Мартина Фаулера» - не заглушки , но до сих пор не понимаю разницу.
Я читал различные статьи о насмешках против окурков в тестировании, в том числе «Мокки Мартина Фаулера» - не заглушки , но до сих пор не понимаю разницу.
Ответы:
огрызок
Я считаю, что самое большое различие заключается в том, что заглушка, которую вы уже написали с заданным поведением. Таким образом, у вас будет класс, который реализует зависимость (абстрактный класс или интерфейс, скорее всего), которую вы фальсифицируете для целей тестирования, и методы будут просто заглушены заданными ответами. Они не будут делать ничего особенного, и вы уже написали бы заглушенный код для этого вне вашего теста.
издеваться
Макет это то, что в рамках вашего теста вы должны настроить с вашими ожиданиями. Макет не настроен заранее, поэтому у вас есть код, который делает это в вашем тесте. Моды в некотором роде определяются во время выполнения, так как код, который устанавливает ожидания, должен выполняться, прежде чем они что-то сделают.
Разница между издевательствами и окурками
Тесты, написанные с помощью макетов, обычно следуют initialize -> set expectations -> exercise -> verify
шаблону тестирования. В то время как предварительно написанная заглушка будет следовать за initialize -> exercise -> verify
.
Сходство между издевательствами и окурками
Цель обоих состоит в том, чтобы исключить тестирование всех зависимостей класса или функции, чтобы ваши тесты были более сфокусированы и проще в том, что они пытаются доказать.
Есть несколько определений объектов, которые не являются реальными. Общий термин - тестовый двойной . Этот термин включает в себя: манекен , подделка , заглушка , макет .
Согласно статье Мартина Фаулера :
- фиктивный объекты передаются, но никогда не используются. Обычно они просто используются для заполнения списков параметров.
- Не настоящие объекты на самом деле имеют рабочие реализации, но обычно используют некоторые ярлыки, которые делают их непригодными для производства (хороший пример - база данных в памяти).
- Столбики обеспечивают постоянные ответы на вызовы, сделанные во время теста, обычно вообще не реагируя ни на что, кроме того, что запрограммировано для теста. Заглушки могут также записывать информацию о вызовах, такую как заглушка шлюза электронной почты, которая запоминает сообщения, которые она «отправила», или, возможно, только сколько сообщений она «отправила».
- Мок - это то, о чем мы здесь говорим: объекты, предварительно запрограммированные с ожиданиями, которые формируют спецификацию вызовов, которые они ожидают получить.
Mocks vs Stubs = Поведенческое тестирование против государственного тестирования
В соответствии с принципом « Тестировать только одну вещь на тест» , в одном тесте может быть несколько заглушек, но обычно используется только один макет.
Тест жизненного цикла с заглушками:
Тест жизненного цикла с макетами:
И тесты на издевательства и окурки дают ответ на вопрос: каков результат?
Тестирование с помощью макетов также интересует: Как был достигнут результат?
Заглушка - это простой поддельный объект. Это просто гарантирует, что тест проходит гладко.
Насмешка - умная заглушка. Вы подтверждаете, что ваш тест проходит через него.
Вот описание каждого, за которым следует пример из реального мира.
Пустышка - просто фиктивные ценности для удовлетворения API
.
Пример : если вы тестируете метод класса, который требует много обязательных параметров в конструкторе, которые не влияют на ваш тест, то вы можете создавать фиктивные объекты с целью создания новых экземпляров класса.
Fake - создать тестовую реализацию класса, которая может зависеть от какой-то внешней инфраструктуры. (Рекомендуется, чтобы ваш модульный тест НЕ взаимодействовал с внешней инфраструктурой.)
Пример : создание ложной реализации для доступа к базе данных, замена ее на
in-memory
коллекцию.
Stub - переопределить методы для возврата жестко закодированных значений, также называемых state-based
.
Пример : ваш тестовый класс зависит от метода, который
Calculate()
занимает 5 минут. Вместо того, чтобы ждать 5 минут, вы можете заменить его реальную реализацию заглушкой, которая возвращает жестко закодированные значения; занимая лишь небольшую долю времени.
Макет - очень похоже, Stub
но interaction-based
скорее не на основе государства. Это означает, что вы не ожидаете отMock
возврата какого-либо значения, а предполагаете, что выполняется определенный порядок вызовов методов.
Пример: вы тестируете класс регистрации пользователя. После звонка
Save
следует позвонитьSendConfirmationEmail
.
Stubs
и Mocks
фактически являются подтипами Mock
, оба заменяют реальную реализацию тестовой реализацией, но по разным, конкретным причинам.
В курсе codeschool.com , Тестирование Rails для Зомби , они дают следующее определение терминов:
огрызок
Для замены метода кодом, который возвращает указанный результат.
издеваться
Заглушка с утверждением, что метод вызывается.
Так, как Шон Копенхейвер описал в своем ответе, разница в том, что издевается над ожиданиями (то есть делает утверждения, о том, как их называют или как).
Заглушки не проваливаются, тестировать можно.
Я думаю, что самый простой и ясный ответ на этот вопрос дан Роем Ошеровом в его книге «Искусство модульного тестирования» (стр. 85).
Самый простой способ сказать, что мы имеем дело с заглушкой, это заметить, что заглушка никогда не сможет пройти проверку. Утверждения, которые используются в тесте, всегда противоречат тестируемому классу.
С другой стороны, тест будет использовать фиктивный объект, чтобы проверить, провалился ли тест или нет. [...]
Опять же, фиктивный объект - это объект, который мы используем, чтобы увидеть, провалился ли тест или нет.
Это означает, что если вы делаете утверждения против фальсификации, это означает, что вы используете фальшивку в качестве насмешки, если вы используете фальшивку только для запуска теста без подтверждения, вы используете фальшивку в качестве заглушки.
Прочитав все объяснения, приведенные выше, я попытаюсь сжать:
Mock - это просто тестирование поведения, проверка того, что определенные методы вызываются. Stub - это тестируемая версия (как таковая) конкретного объекта.
Что вы имеете в виду путь Apple?
Если вы сравните это с отладкой:
Stub - это как проверка того, что метод возвращает правильное значение
Mock - это то же самое, что войти в метод и убедиться, что все внутри правильно, прежде чем возвращать правильное значение.
Используя ментальную модель действительно помогло мне понять это, а не все объяснения и статьи, которые не совсем «утонули».
Представьте, что у вашего ребенка на столе стеклянная тарелка, и он начинает с ней играть. Теперь вы боитесь, что оно сломается. Итак, вместо этого вы даете ему пластиковую тарелку. Это будет Mock (такое же поведение, тот же интерфейс, «более мягкая» реализация).
Теперь, скажем, у вас нет замены пластика, поэтому вы объясняете: «Если вы продолжите играть с ним, он сломается!». Это заглушка , вы заранее указали заранее заданное состояние.
Пустышки будут развилкой он даже не использовал ... и Spy может быть что - то вроде обеспечения такого же объяснения вы уже использовали это работало.
Я думаю, что самое важное различие между ними - это их намерения.
Позвольте мне попытаться объяснить это в ПОЧЕМУ заглушка против ПОЧЕМУ насмешливый
Предположим, я пишу тестовый код для общедоступного контроллера временной шкалы моего mac-клиента
Вот тестовый образец кода
twitter_api.stub(:public_timeline).and_return(public_timeline_array)
client_ui.should_receive(:insert_timeline_above).with(public_timeline_array)
controller.refresh_public_timeline
Написав макет, вы обнаружите взаимосвязь взаимодействия объектов, проверив, что ожидания ожидаются, а заглушка только имитирует поведение объекта.
Я предлагаю прочитать эту статью, если вы пытаетесь узнать больше о макетах: http://jmock.org/oopsla2004.pdf
Быть очень понятным и практичным:
Заглушка: класс или объект, который реализует методы класса / объекта, которые нужно подделать и всегда возвращает то, что вы хотите.
Пример в JavaScript:
var Stub = {
method_a: function(param_a, param_b){
return 'This is an static result';
}
}
Макет: то же самое для заглушки, но он добавляет некоторую логику, которая «проверяет», когда вызывается метод, так что вы можете быть уверены, что какая-то реализация вызывает этот метод.
Как говорит @mLevan, представьте в качестве примера, что вы тестируете класс регистрации пользователей. После вызова Save, он должен вызвать SendConfirmationEmail.
Очень глупый код Пример:
var Mock = {
calls: {
method_a: 0
}
method_a: function(param_a, param_b){
this.method_a++;
console.log('Mock.method_a its been called!');
}
}
Этот слайд объясняет основные отличия очень хорошо.
* Из CSE 403, лекция 16, Вашингтонский университет (слайд, созданный Марти Степпом)
Мне нравится объяснение Роя Ошерова [ссылка на видео] .
Каждый созданный класс или объект является подделкой. Это издевательство, если вы проверяете звонки против него. В противном случае это заглушка.
давайте посмотрим тест двойников:
Stub : Stub - это объект, который содержит предопределенные данные и использует их для ответа на вызовы во время тестов. Например : объект, который должен получить некоторые данные из базы данных, чтобы ответить на вызов метода.
Насмешки : издевательства - это объекты, которые регистрируют звонки, которые они получают. В тестовом утверждении мы можем проверить по Mocks, что все ожидаемые действия были выполнены. Такие как : функциональность, которая вызывает службу отправки электронной почты. для большего просто проверьте это .
подделка это общий термин , который может быть использован для описания либо окурка или фиктивный объекта (рукописный или иным образом ), потому что они оба выглядит как реальный объект.
Является ли подделка заглушкой или макетом, зависит от того, как она используется в текущем тесте. Если он используется для проверки взаимодействия (утверждается против), это фиктивный объект. В противном случае, это заглушка.
Подделки удостоверяются, что тест проходит гладко. Это означает, что читатель вашего будущего теста поймет, как будет вести себя фальшивый объект, без необходимости читать его исходный код (без необходимости зависеть от внешнего ресурса).
Что означает тестовый прогон?
Пример в приведенном ниже коде:
public void Analyze(string filename)
{
if(filename.Length<8)
{
try
{
errorService.LogError("long file entered named:" + filename);
}
catch (Exception e)
{
mailService.SendEMail("admin@hotmail.com", "ErrorOnWebService", "someerror");
}
}
}
Вы хотите проверить mailService.SendEMail () , для этого вам нужно смоделировать исключение в вашем тестовом методе, поэтому вам просто нужно создать класс Fake Stub errorService для имитации этого результата, тогда ваш тестовый код сможет протестировать mailService.SendEMail () метод. Как видите, вам нужно смоделировать результат из другого класса External Dependency ErrorService.
Сразу же из статьи Mock Roles, а не Objects , разработчиками jMock:
Заглушки - это фиктивные реализации производственного кода, которые возвращают постоянные результаты. Поддельные объекты выступают в роли заглушек, но также включают в себя утверждения для контроля взаимодействия целевого объекта с его соседями.
Итак, основные отличия:
Подводя итог, мы также пытаемся рассеять путаницу из названия статьи Фаулера : издевательства - это заглушки, но не только заглушки .
Я читал «Искусство модульного тестирования» и наткнулся на следующее определение:
Подделка это общий термин , который может быть использован для описания либо окурка или фиктивный объекта (рукописный или иным образом ), потому что они оба выглядит как реальный объект. Является ли подделка заглушкой или макетом, зависит от того, как она используется в текущем тесте. если он используется для проверки взаимодействия (утверждается против), это фиктивный объект . В противном случае, это заглушка .
Я наткнулся на эту интересную статью UncleBob The Little Mocker . Это объясняет всю терминологию в очень простой для понимания форме, поэтому она полезна для начинающих. Статья Мартина Фаулерса очень читаема, особенно для таких начинающих, как я.
Заглушка помогает нам запустить тест. Как? Это дает значения, которые помогают запустить тест. Эти значения сами по себе не являются реальными, и мы создали эти значения только для запуска теста. Например, мы создаем HashMap, чтобы дать нам значения, которые аналогичны значениям в таблице базы данных. Поэтому вместо непосредственного взаимодействия с базой данных мы взаимодействуем с Hashmap.
Мок это поддельный объект, который запускает тест. где мы утверждаем.
Ниже приведен пример использования mocks против заглушек с использованием C # и Moq framework. В Moq нет специального ключевого слова для Stub, но вы можете использовать объект Mock для создания заглушек.
namespace UnitTestProject2
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
[TestClass]
public class UnitTest1
{
/// <summary>
/// Test using Mock to Verify that GetNameWithPrefix method calls Repository GetName method "once" when Id is greater than Zero
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsTwelve_GetNameCalledOnce()
{
// Arrange
var mockEntityRepository = new Mock<IEntityRepository>();
mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
var entity = new EntityClass(mockEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(12);
// Assert
mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Once);
}
/// <summary>
/// Test using Mock to Verify that GetNameWithPrefix method doesn't call Repository GetName method when Id is Zero
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsZero_GetNameNeverCalled()
{
// Arrange
var mockEntityRepository = new Mock<IEntityRepository>();
mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
var entity = new EntityClass(mockEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(0);
// Assert
mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Never);
}
/// <summary>
/// Test using Stub to Verify that GetNameWithPrefix method returns Name with a Prefix
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsTwelve_ReturnsNameWithPrefix()
{
// Arrange
var stubEntityRepository = new Mock<IEntityRepository>();
stubEntityRepository.Setup(m => m.GetName(It.IsAny<int>()))
.Returns("Stub");
const string EXPECTED_NAME_WITH_PREFIX = "Mr. Stub";
var entity = new EntityClass(stubEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(12);
// Assert
Assert.AreEqual(EXPECTED_NAME_WITH_PREFIX, name);
}
}
public class EntityClass
{
private IEntityRepository _entityRepository;
public EntityClass(IEntityRepository entityRepository)
{
this._entityRepository = entityRepository;
}
public string Name { get; set; }
public string GetNameWithPrefix(int id)
{
string name = string.Empty;
if (id > 0)
{
name = this._entityRepository.GetName(id);
}
return "Mr. " + name;
}
}
public interface IEntityRepository
{
string GetName(int id);
}
public class EntityRepository:IEntityRepository
{
public string GetName(int id)
{
// Code to connect to DB and get name based on Id
return "NameFromDb";
}
}
}
Точка зрения Stub и Mock:
Stub - это фиктивная реализация, выполненная пользователем в статическом виде, то есть при написании Stub кода реализации. Таким образом, он не может обрабатывать определение сервиса и динамическое состояние. Обычно это делается в платформе JUnit без использования фреймворка.
Mock также является фиктивной реализацией, но ее реализация осуществляется динамическим образом с использованием Mocking Frameworks, таких как Mockito. Таким образом, мы можем обрабатывать определение условий и сервисов как динамический путь, т.е. макеты могут создаваться динамически из кода во время выполнения. Таким образом, используя mock, мы можем реализовать Stubs динамически.
Плюс полезные ответы, одна из самых мощных точек использования Mocks, чем Subs
Если соавтор [от которого зависит основной код] не находится под нашим контролем (например, из сторонней библиотеки),
в этом случае заглушку труднее написать, чем смоделировать .
Я использовал примеры Python в своем ответе, чтобы проиллюстрировать различия.
Stub - Stubbing - это метод разработки программного обеспечения, используемый для реализации методов классов на ранних этапах жизненного цикла разработки. Они обычно используются в качестве заполнителей для реализации известного интерфейса, где интерфейс завершен или известен, но реализация еще не известна или не завершена. Вы начинаете с заглушек, что просто означает, что вы только записываете определение функции и оставляете фактический код на потом. Преимущество в том, что вы не забудете методы и сможете продолжать думать о своем дизайне, видя его в коде. Вы также можете сделать так, чтобы ваша заглушка возвращала статический ответ, чтобы этот ответ мог быть немедленно использован другими частями вашего кода. Объекты-заглушки дают правильный ответ, но он статический, независимо от того, какой ввод вы передаете, вы всегда получите один и тот же ответ:
class Foo(object):
def bar1(self):
pass
def bar2(self):
#or ...
raise NotImplementedError
def bar3(self):
#or return dummy data
return "Dummy Data"
издеваться объекты используются в имитационных тестовых случаях, они проверяют, что определенные методы вызываются для этих объектов. Поддельные объекты - это моделируемые объекты, которые имитируют поведение реальных объектов контролируемыми способами. Обычно вы создаете фиктивный объект для проверки поведения другого объекта. Мок позволяет нам моделировать ресурсы, которые либо недоступны, либо слишком громоздки для модульного тестирования.
mymodule.py:
import os
import os.path
def rm(filename):
if os.path.isfile(filename):
os.remove(filename)
test.py:
from mymodule import rm
import mock
import unittest
class RmTestCase(unittest.TestCase):
@mock.patch('mymodule.os')
def test_rm(self, mock_os):
rm("any path")
# test that rm called os.remove with the right parameters
mock_os.remove.assert_called_with("any path")
if __name__ == '__main__':
unittest.main()
Это очень простой пример, который просто запускает rm и подтверждает параметр, с которым он был вызван. Вы можете использовать mock с объектами, а не только с функциями, как показано здесь, и вы также можете возвращать значение, так что фиктивный объект может использоваться для замены заглушки для тестирования.
Еще подробно unittest.mock , заметка в python 2.x mock не включена в unittest, но является загружаемым модулем, который можно загрузить через pip (pip install mock).
Я также прочитал «Искусство модульного тестирования» Роя Ошерова, и я думаю, было бы здорово, если бы подобная книга была написана с использованием примеров на Python и Python. Если кто-нибудь знает о такой книге, пожалуйста, поделитесь. Ура :)
Заглушка - это фальшивый объект, созданный для целей тестирования. Макет - это заглушка, которая записывает, действительно ли произошли ожидаемые вызовы.
Заглушка - это пустая функция, которая используется во избежание необработанных исключений во время тестов:
function foo(){}
Макет - это искусственная функция, которая используется во избежание зависимостей ОС, среды или оборудования во время тестов:
function foo(bar){ window = this; return window.toString(bar); }
С точки зрения утверждений и состояния:
Ссылки
там много правильных ответов, но я думаю, стоит упомянуть эту форму, дядя Боб: https://8thlight.com/blog/uncle-bob/2014/05/14/TheLittleMocker.html
лучшее объяснение с примерами!
Макет - это и технический, и функциональный объект.
Макет технический . Он действительно создается библиотекой-имитатором (EasyMock, JMockit и совсем недавно Mockito известны этим) благодаря генерации байт-кода .
Пробная реализация генерируется в пути , где мы могли бы инструмент его возвращать значение специфического , когда метод вызывается , но и некоторые другие вещи , такие как проверка , что пробный метод был вызван с некоторыми параметрами конкретных (строгая проверка) или каким - либо параметрами ( строгой проверки нет).
Пример издевательства:
@Mock Foo fooMock
Запись поведения:
when(fooMock.hello()).thenReturn("hello you!");
Проверка вызова:
verify(fooMock).hello()
Это явно не естественный способ создания / переопределения класса / поведения Foo. Вот почему я ссылаюсь на технический аспект.
Но макет также функционален, потому что это экземпляр класса, который мы должны изолировать от SUT. И с записанным поведением на нем, мы могли бы использовать его в SUT таким же образом, как и с заглушкой.
Заглушка - это просто функциональный объект: это экземпляр класса, который нам нужно изолировать от SUT, и все. Это означает, что и класс-заглушка, и все приспособления поведения, необходимые во время наших модульных тестов, должны быть определены явно.
Например, для заглушки hello()
потребуется создать подкласс Foo
класса (или реализовать его интерфейс, который у него есть) и переопределить hello()
:
public class HelloStub extends Hello{
public String hello {
return "hello you!";
}
}
Если другой тестовый сценарий требует другого возврата значения, нам, вероятно, потребуется определить общий способ установки возврата:
public class HelloStub extends Hello{
public HelloStub(String helloReturn){
this.helloReturn = helloReturn;
}
public String hello {
return helloReturn;
}
}
Другой сценарий: если бы у меня был метод побочного эффекта (без возврата), и я бы проверил, что этот метод был вызван, я, вероятно, должен был бы добавить логический или счетчик в класс-заглушку, чтобы подсчитать, сколько раз метод был вызван.
Вывод
Заглушка часто требует много накладных расходов / кода для написания вашего модульного теста. То, что макет предотвращает благодаря предоставлению функций записи / проверки из коробки.
Вот почему в настоящее время подход с заглушками редко используется на практике с появлением превосходных макетов библиотек.
О статье Мартина Фаулера: я не думаю, что я программист, когда я использую издевательства и избегаю заглушек.
Но я использую mock, когда это действительно необходимо (раздражающие зависимости), и я предпочитаю тестирование срезов и миниинтеграционные тесты, когда я тестирую класс с зависимостями, чье использование было бы непроизводительным.