Я новичок в модульном тестировании, и я постоянно слышу слова «фиктивные объекты». С точки зрения непрофессионала, может ли кто-нибудь объяснить, что такое фиктивные объекты и для чего они обычно используются при написании модульных тестов?
Я новичок в модульном тестировании, и я постоянно слышу слова «фиктивные объекты». С точки зрения непрофессионала, может ли кто-нибудь объяснить, что такое фиктивные объекты и для чего они обычно используются при написании модульных тестов?
Ответы:
Поскольку вы говорите, что вы новичок в модульном тестировании, и попросили фиктивные объекты в «терминах непрофессионала», я попробую пример неспециалистов.
Представьте себе модульное тестирование для этой системы:
cook <- waiter <- customer
Как правило, легко представить тестирование низкоуровневого компонента, такого как cook
:
cook <- test driver
Тест-водитель просто заказывает разные блюда и проверяет, что повар возвращает правильное блюдо для каждого заказа.
Труднее протестировать средний компонент, такой как официант, который использует поведение других компонентов. Наивный тестировщик может протестировать компонент официанта так же, как мы тестировали компонент готовки:
cook <- waiter <- test driver
Тест-водитель заказывает разные блюда и гарантирует, что официант вернет правильное блюдо. К сожалению, это означает, что этот тест компонента официанта может зависеть от правильного поведения компонента приготовления. Эта зависимость еще хуже, если у компонента кулинарии есть какие-то недружелюбные к тесту характеристики, такие как недетерминированное поведение (меню включает сюрприз шеф-повара как блюдо), множество зависимостей (повар не готовит без всего своего персонала) или много ресурсы (некоторые блюда требуют дорогих ингредиентов или час, чтобы приготовить).
Поскольку это тест официанта, в идеале мы хотим проверить только официанта, а не повара. В частности, мы хотим убедиться, что официант правильно передает заказ клиента повару и правильно доставляет еду повара клиенту.
Модульное тестирование означает независимое тестирование модулей, поэтому лучшим подходом было бы изолировать тестируемый компонент (официант), используя то, что Фаулер называет тестовыми двойниками (манекены, заглушки, подделки, макеты) .
-----------------------
| |
v |
test cook <- waiter <- test driver
Здесь, тестовый повар "в сговоре" с тестовым водителем. В идеале, тестируемая система разработана таким образом , что тест повар может быть легко замещен ( впрыскивается ) для работы с официанта без изменения коды производства (например , без изменения коды официанта).
Теперь тест-кулинар (test double) может быть реализован разными способами:
См. Статью Фаулера для получения более подробной информации о подделках, окурках, фиктивных и манекенах , но сейчас давайте сосредоточимся на фальшивом кулинаре.
-----------------------
| |
v |
mock cook <- waiter <- test driver
Большая часть модульного тестирования компонента официанта фокусируется на том, как официант взаимодействует с компонентом повара. Подход, основанный на имитации, фокусируется на том, чтобы полностью определить, что такое правильное взаимодействие, и определить, когда оно идет не так.
Имитируемый объект заранее знает, что должно происходить во время теста (например, какие вызовы его методов будут вызваны и т. Д.), И имитирующий объект знает, как он должен реагировать (например, какое возвращаемое значение предоставить). Насмешка покажет, отличается ли то, что действительно происходит, от того, что должно произойти. Пользовательский фиктивный объект может быть создан с нуля для каждого тестового примера, чтобы выполнить ожидаемое поведение для этого тестового примера, но фреймворк стремится к тому, чтобы такая спецификация поведения была четко и легко указана непосредственно в тестовом примере.
Разговор о ложном тесте может выглядеть так:
Тест драйвер для макетом повара : ожидать горячий заказ собаки и дать ему эту фиктивную хот - дог в ответ
водитель - испытатель (выдавая себя за клиента) к официанту : Я хотел хот - дог , пожалуйста ,
официанта , чтобы издеваться готовить : 1 хот - дог , пожалуйста ,
макет готовить к официанту : заказ до: 1 хот - дог готов (дает фиктивный хот - дог на официанта)
официантов , чтобы водитель - испытатель : вот твой хот-дог (дает фиктивного хот-дога тест-драйверу)тест-водитель : ТЕСТ УСПЕШЕН!
Но так как наш официант новичок, вот что может произойти:
Тест драйвер для макетом повара : ожидать горячий заказ собаки и дать ему эту фиктивную хот - дог в ответ
тест-водитель (выдавая себя за клиента) официанту : я хотел бы хот-дога, пожалуйста,
официант, чтобы издеваться над поваром : 1 гамбургер, пожалуйста,
насмешливый повар останавливает тест: мне сказали ожидать заказа хот-дога!Водитель-испытатель отмечает проблему: ТЕСТ НЕ ПРОВЕРЕН! - официант изменил порядок
или
Тест драйвер для макетом повара : ожидать горячий заказ собаки и дать ему эту фиктивную хот - дог в ответ
водитель - испытатель (выдавая себя за клиента) к официанту : Я хотел хот - дог , пожалуйста ,
официанта , чтобы издеваться готовить : 1 хот - дог , пожалуйста ,
макет готовить к официанту : заказ до: 1 хот - дог готов (дает фиктивный хот - дог на официанта)
официантов , чтобы водитель - испытатель : вот ваш картофель фри (дает картофель фри из какого-то другого заказа для тестирования водителя)Водитель-испытатель замечает неожиданный картофель фри: ТЕСТ НЕ ПРОВЕРЕН! официант вернул неправильное блюдо
Может быть трудно ясно увидеть разницу между фиктивными объектами и заглушками без контрастирующего примера на основе заглушки, но этот ответ уже слишком длинный :-)
Также обратите внимание на то, что это довольно упрощенный пример, и что макеты-фреймворки допускают некоторые довольно сложные спецификации ожидаемого поведения компонентов для поддержки всесторонних тестов. Есть много материала о ложных объектах и насмешливых структурах для получения дополнительной информации.
Mock Object - это объект, который заменяет реальный объект. В объектно-ориентированном программировании фиктивные объекты - это симулированные объекты, которые имитируют поведение реальных объектов контролируемыми способами.
Программист обычно создает фиктивный объект для проверки поведения какого-либо другого объекта, почти так же, как автомобильный конструктор использует манекен для краш-теста для имитации динамического поведения человека при столкновении с транспортным средством.
http://en.wikipedia.org/wiki/Mock_object
Поддельные объекты позволяют вам создавать тестовые сценарии, не используя большие громоздкие ресурсы, такие как базы данных. Вместо вызова базы данных для тестирования, вы можете смоделировать вашу базу данных, используя фиктивный объект в ваших модульных тестах. Это освобождает вас от необходимости устанавливать и разрушать настоящую базу данных, просто чтобы протестировать один метод в вашем классе.
Слово «макет» иногда ошибочно используется как «заглушка». Различия между двумя словами описаны здесь. По существу, макет - это объект-заглушка, который также включает ожидания (то есть «утверждения») для правильного поведения тестируемого объекта / метода.
Например:
class OrderInteractionTester...
public void testOrderSendsMailIfUnfilled() {
Order order = new Order(TALISKER, 51);
Mock warehouse = mock(Warehouse.class);
Mock mailer = mock(MailService.class);
order.setMailer((MailService) mailer.proxy());
mailer.expects(once()).method("send");
warehouse.expects(once()).method("hasInventory")
.withAnyArguments()
.will(returnValue(false));
order.fill((Warehouse) warehouse.proxy());
}
}
Обратите внимание, что объекты warehouse
и mailer
mock запрограммированы с ожидаемыми результатами.
Поддельные объекты - это моделируемые объекты, которые имитируют поведение реальных. Обычно вы пишете фиктивный объект, если:
Объект Mock - это один из видов Test Double . Вы используете mockobjects для тестирования и проверки протокола / взаимодействия тестируемого класса с другими классами.
Как правило, вы будете в некотором роде «программными» или «записывать» ожидания: вызовы методов, которые вы ожидаете, что ваш класс сделает с базовым объектом.
Допустим, например, что мы тестируем сервисный метод для обновления поля в виджете. И что в вашей архитектуре есть WidgetDAO, который имеет дело с базой данных. Разговор с базой данных идет медленно, его настройка и последующая очистка усложняются, поэтому мы смоделируем WidgetDao.
давайте подумаем, что должен делать сервис: он должен получить виджет из базы данных, сделать что-то с ним и сохранить его снова.
Таким образом, в псевдо-языке с псевдо-фиктивной библиотекой у нас будет что-то вроде:
Widget sampleWidget = new Widget();
WidgetDao mock = createMock(WidgetDao.class);
WidgetService svc = new WidgetService(mock);
// record expected calls on the dao
expect(mock.getById(id)).andReturn(sampleWidget);
expect(mock.save(sampleWidget);
// turn the dao in replay mode
replay(mock);
svc.updateWidgetPrice(id,newPrice);
verify(mock); // verify the expected calls were made
assertEquals(newPrice,sampleWidget.getPrice());
Таким образом, мы можем легко протестировать разработку классов, которые зависят от других классов.
Я настоятельно рекомендую отличную статью Мартина Фаулера, в которой объясняется, что такое макеты и чем они отличаются от заглушек.
При модульном тестировании какой-либо части компьютерной программы в идеале вы хотите протестировать только поведение этой конкретной части.
Например, посмотрите на псевдокод ниже из воображаемого фрагмента программы, которая использует другую программу для вызова функции печати чего-либо:
If theUserIsFred then
Call Printer(HelloFred)
Else
Call Printer(YouAreNotFred)
End
Если вы тестировали это, вы бы в основном хотели протестировать ту часть, которая проверяет, является ли пользователь Фредом или нет. Вы действительно не хотите проверять Printer
часть вещей. Это было бы еще одним испытанием.
Это то место , где появляются ложные объекты. Они притворяются другими вещами. В этом случае вы бы использовали макет, Printer
чтобы он действовал так же, как настоящий принтер, но не делал бы неудобных вещей, таких как печать.
Есть несколько других типов притворных объектов, которые вы можете использовать, но не Mocks. Главное, что делает Mocks Mocks, - это то, что их можно настроить с учетом поведения и ожиданий.
Ожидания позволяют вашей Mock выдавать ошибку, если она используется неправильно. Таким образом, в приведенном выше примере вы можете быть уверены, что Printer вызывается с HelloFred в тестовом примере "user is Fred". Если этого не произойдет, ваш издеватель может предупредить вас.
Поведение в издевательствах означает, что, например, ваш код сделал что-то вроде:
If Call Printer(HelloFred) Returned SaidHello Then
Do Something
End
Теперь вы хотите проверить, что делает ваш код при вызове Printer и возвращает SaidHello, поэтому вы можете настроить Mock на возврат SaidHello при вызове с HelloFred.
Один хороший ресурс вокруг этого Мартин Fowlers разместить Mocks ли не Столбики
Ложные и тупые объекты являются важной частью модульного тестирования. На самом деле они проходят долгий путь, чтобы убедиться, что вы тестируете юниты , а не группы юнитов.
Короче говоря, вы используете заглушки, чтобы сломать зависимость SUT (тестируемая система) от других объектов и макетов, чтобы сделать это, и убедиться, что SUT вызвал определенные методы / свойства для зависимости. Это восходит к фундаментальным принципам модульного тестирования - то, что тесты должны быть легко читаемыми, быстрыми и не требующими настройки, что может подразумевать использование всех реальных классов.
Как правило, в вашем тесте может быть несколько заглушек, но у вас должен быть только один макет. Это потому, что целью макета является проверка поведения, а ваш тест должен проверять только одну вещь.
Простой сценарий с использованием C # и Moq:
public interface IInput {
object Read();
}
public interface IOutput {
void Write(object data);
}
class SUT {
IInput input;
IOutput output;
public SUT (IInput input, IOutput output) {
this.input = input;
this.output = output;
}
void ReadAndWrite() {
var data = input.Read();
output.Write(data);
}
}
[TestMethod]
public void ReadAndWriteShouldWriteSameObjectAsRead() {
//we want to verify that SUT writes to the output interface
//input is a stub, since we don't record any expectations
Mock<IInput> input = new Mock<IInput>();
//output is a mock, because we want to verify some behavior on it.
Mock<IOutput> output = new Mock<IOutput>();
var data = new object();
input.Setup(i=>i.Read()).Returns(data);
var sut = new SUT(input.Object, output.Object);
//calling verify on a mock object makes the object a mock, with respect to method being verified.
output.Verify(o=>o.Write(data));
}
В приведенном выше примере я использовал Moq для демонстрации заглушек и насмешек. Moq использует один и тот же класс для обоих - Mock<T>
что делает его немного запутанным. Независимо от того, во время выполнения, тест не будет выполнен, если output.Write
не вызывается с данными как parameter
, тогда как неудачный вызов input.Read()
не провалит его.
Как еще один ответ, предложенный через ссылку на « Насмешки не заглушки », mocks - это форма «test double», которую можно использовать вместо реального объекта. Что отличает их от других форм тестовых двойников, таких как объекты-заглушки, заключается в том, что другие тестовые двойники предлагают проверку состояния (и, возможно, моделирование), тогда как макеты предлагают проверку поведения (и, возможно, моделирование).
С заглушкой вы можете вызывать несколько методов заглушки в любом порядке (или даже многократно) и определять успех, если заглушка получила значение или состояние, которое вы намеревались. Напротив, фиктивный объект ожидает вызова очень специфических функций в определенном порядке и даже определенное количество раз. Тест с фиктивным объектом будет считаться «неудачным» просто потому, что методы были вызваны в другой последовательности или количестве - даже если фиктивный объект имел правильное состояние после завершения теста!
Таким образом, фиктивные объекты часто считаются более тесно связанными с кодом SUT, чем объекты-заглушки. Это может быть хорошо или плохо, в зависимости от того, что вы пытаетесь проверить.
Частью использования фиктивных объектов является то, что они не должны быть реально реализованы в соответствии со спецификацией. Они могут просто дать фиктивные ответы. Например, если вам нужно реализовать компоненты A и B, и оба «вызывают» (взаимодействовать) друг с другом, то вы не можете проверить A, пока не будет реализован B, и наоборот. В тестовой разработке это проблема. Таким образом, вы создаете фиктивные («фиктивные») объекты для A и B, которые очень просты, но дают некоторый ответ, когда с ними взаимодействуют. Таким образом, вы можете реализовать и протестировать A, используя фиктивный объект для B.
Для php и phpunit это хорошо объяснено в документации phpunit. смотрите здесь документацию phpunit
В простом слове mocking object - это просто фиктивный объект вашего оригинала, который возвращает свое возвращаемое значение, это возвращаемое значение может быть использовано в тестовом классе
Это одна из основных перспектив юнит-тестов. да, вы пытаетесь протестировать свою единицу кода, и результаты вашего теста не должны быть связаны с поведением других компонентов или объектов. таким образом, вы должны смоделировать их, используя объекты Mock с некоторым упрощенным соответствующим ответом.