Я читав різні статті про знущання проти заїкання в тестуванні, включаючи "Наслідки Мартіна Фаулера не є заготовками" , але все ще не розумію різниці.
Я читав різні статті про знущання проти заїкання в тестуванні, включаючи "Наслідки Мартіна Фаулера не є заготовками" , але все ще не розумію різниці.
Відповіді:
Стуб
Я вважаю, що найбільша відмінність полягає в тому, що заглушка, яку ви вже написали з заздалегідь визначеною поведінкою. Таким чином, у вас був би клас, який реалізує залежність (найімовірніше, абстрактний клас або інтерфейс), з якою ви підробляєтесь для тестування, і методи будуть просто заточені з заданими відповідями. Вони б нічого не фантазували, і ви вже написали стертий код поза вашим тестом.
Знущаються
Знущання - це те, що у рамках тесту ви повинні налаштувати свої очікування. Макет не встановлюється заздалегідь визначеним чином, тому у вас є код, який робить це у вашому тесті. Макети певним чином визначаються під час виконання, оскільки код, який встановлює очікування, повинен працювати, перш ніж щось робити.
Різниця між макетами та заглушками
Тести, написані з макетами, зазвичай відповідають initialize -> set expectations -> exercise -> verify
схемі тестування. Поки заздалегідь написана заглушка буде слідувати an initialize -> exercise -> verify
.
Подібність між Макетами та Стубсами
Мета обох - усунути тестування всіх залежностей класу чи функції, щоб ваші тести були більш зосередженими та простішими у тому, що вони намагаються довести.
Існує кілька визначень об'єктів, які не є реальними. Загальний термін - тест подвійний . Цей термін охоплює: манекен , фейк , заглушка , глузування .
Відповідно до статті Мартіна Фаулера :
- Об'єкти пустушки передаються навколо, але фактично ніколи не використовуються. Зазвичай вони просто використовуються для заповнення списків параметрів.
- Підроблені об’єкти насправді мають робочі реалізації, але зазвичай вони мають деякий ярлик, який робить їх непридатними для виробництва (хороший приклад в базі даних пам'яті).
- Заглушки надають консервовані відповіді на дзвінки, здійснені під час тесту, зазвичай взагалі не відповідають на те, що передбачено для тесту. Заглушки можуть також записувати інформацію про дзвінки, наприклад, заглушку шлюзу електронної пошти, яка запам’ятовує повідомлення, які вона надіслала, або, можливо, лише скільки повідомлень вона надіслала.
- Знущання - це те, про що ми говоримо тут: об’єкти, заздалегідь запрограмовані на очікування, які формують специфікацію дзвінків, які вони очікують приймати.
Mocks vs Stubs = Тестування на поведінку та державне тестування
Згідно з принципом Тестування лише одне на тест , в одному тесті може бути кілька заглушок, але, як правило, є лише один макет.
Тестовий життєвий цикл за допомогою заглушок:
Тест життєвого циклу з макетами:
Тестування як глузувань, так і заглушок дає відповідь на запитання: Який результат?
Тестування з макетами також цікавить: Як досягнутий результат?
Заглушка - простий підроблений предмет. Це просто гарантує, що тест проходить безперебійно.
Макет - розумніший заглушок. Ви перевіряєте, що ваш тест проходить через нього.
Ось опис кожного з них, а далі - зразок реального світу.
Манекен - просто нечіткі значення для задоволення API
.
Приклад : Якщо ви тестуєте метод класу, який вимагає багатьох обов'язкових параметрів у конструкторі, які не впливають на ваш тест, ви можете створювати фіктивні об’єкти для створення нових екземплярів класу.
Підробка - створити тестову реалізацію класу, який може залежати від якоїсь зовнішньої інфраструктури. (Доброю практикою є те, що ваш тестовий пристрій НЕ насправді взаємодіє із зовнішньою інфраструктурою.)
Приклад : Створіть підроблену реалізацію для доступу до бази даних, замініть її на
in-memory
колекцію.
Заглушка - методи заміщення для повернення твердо кодованих значень, які також називаються state-based
.
Приклад : Ваш тестовий клас залежить від способу, який
Calculate()
потребує 5 хвилин. Замість того, щоб чекати 5 хвилин, ви можете замінити його реальну реалізацію на заглушку, яка повертає жорстко закодовані значення; займаючи лише невелику частину часу.
Макет - дуже схожий, Stub
але interaction-based
не на державному рівні. Це означає, що ви не розраховуєте Mock
повернути якесь значення, але припустити, що здійснюється конкретний порядок викликів методів.
Приклад: Ви тестуєте клас реєстрації користувача. Після дзвінка
Save
слід зателефонуватиSendConfirmationEmail
.
Stubs
і Mocks
насправді є підтипами Mock
, обидва змінюють реальну реалізацію з реалізацією тесту, але з різних, конкретних причин.
У курсі codechool.com тестування рейлів на зомбі , вони дають таке визначення термінів:
Стуб
Для заміни методу кодом, який повертає заданий результат.
Знущаються
Заглушка з твердженням, що метод називається.
Так, як у своїй відповіді описав Шон Копенгавер, різниця полягає в тому, що глузуючі задають очікування (тобто роблять твердження про те, чи їх називають).
Заглушки не провалюють ваші тести, знущаються.
Я думаю, що найпростішу і зрозумілішу відповідь на це питання дає Рой Ошерово у своїй книзі «Мистецтво одиничного тестування» (стор. 85)
Найпростіший спосіб сказати, що ми маємо справу з заглушкою - помітити, що заглушка ніколи не може провалити тест. Твердження, які використовуються в тесті, завжди відповідають тестуваному класу.
З іншого боку, тест використовуватиме макетний об’єкт, щоб перевірити, чи не вдався тест чи ні. [...]
Знову ж таки, об'єкт макету - це той об’єкт, який ми використовуємо, щоб побачити, невдалий тест чи ні.
Це означає, що якщо ви робите твердження проти підробки, це означає, що ви використовуєте підробку як макет, якщо ви використовуєте підробку лише для запуску тесту без твердження над ним, ви використовуєте підробку як заглушку.
Читаючи всі пояснення вище, дозвольте спробувати ущільнитись:
Якщо ви порівнюєте це з налагодженням:
Stub - це як переконатися, що метод повертає правильне значення
Макет - це як насправді вступити в метод і переконатися, що всередині все правильно, перш ніж повернути правильне значення.
Використання ментальної моделі дійсно допомогло мені зрозуміти це, а не всі пояснення та статті, які не зовсім «занурилися».
Уявіть, що ваша дитина має на столі скляну тарілку, і він починає грати з нею. Тепер ти боїшся, що вона зламається. Отже, ви даєте йому замість нього пластикову тарілку. Це був би Mock (та ж поведінка, той же інтерфейс, "м'якша" реалізація).
Тепер скажіть, що у вас немає пластикової заміни, тому ви пояснюєте "Якщо ви продовжите грати з ним, вона зламається!". Це Стуб , ви заздалегідь надали стан заздалегідь.
Пустушки будуть розвилкою він навіть не використовував ... і Spy може бути що - щось на зразок забезпечення такого ж пояснення ви вже використовували це працювало.
Я думаю, що найважливіша різниця між ними - їхні наміри.
Дозвольте спробувати пояснити це, ЧОМУ заглушка vs. ЧОМУ глузування
Припустимо, я пишу тестовий код для загального контролера загальної шкали часу свого клієнта mac twitter
Ось код тестового зразка
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
Щоб бути дуже чітким і практичним:
Stub: Клас або об’єкт, який реалізує методи класу / об'єкта, які підробляють, і завжди повертає те, що ви хочете.
Приклад у JavaScript:
var Stub = {
method_a: function(param_a, param_b){
return 'This is an static result';
}
}
Макет: Це ж заглушка, але вона додає певної логіки, яка "перевіряє", коли метод викликається, так що ви можете бути впевнені, що якась реалізація викликає цей метод.
Як говорить @mLevan, уявіть, як приклад, що ви тестуєте клас реєстрації користувача. Після дзвінка Зберегти, він повинен зателефонувати 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, Університет Вашингтона (слайд створений "Марті Степ")
Мені подобається пояснення, викладене Роєм Ошеровим [відео посилання] .
Кожен створений клас або об’єкт - це підробка. Це глузування, якщо ви перевіряєте дзвінки проти нього. Інакше його заглушка.
перегляньте тестові парні ігри:
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 (). Як ви бачите, вам потрібно імітувати результат, який є з іншого класу ErrorService External Dependency.
Розробники jMock прямо з паперових макетних ролей, а не об'єктів :
Заглушки - це фіктивні варіанти реалізації виробничого коду, які повертають консервовані результати. Обметні об'єкти виступають в якості заглушок, але також містять твердження, що підтверджують взаємодію цільового об'єкта з сусідами.
Отже, основні відмінності:
Підводячи підсумок, одночасно намагаючись розвіяти плутанину з назви статті Фаулера : макети - це заглушки, але вони не лише заглушки .
Я читав «Мистецтво одиничного тестування» і натрапляв на таке визначення:
Підробка це загальний термін , який може бути використаний для опису або недопалка або фіктивний об'єкта (рукописний чи іншим чином ), тому що вони обидва виглядає як реальний об'єкт. Чи підробка є заглушкою чи макетом, залежить від того, як вона використовується в поточному тесті. якщо він використовується для перевірки взаємодії (стверджується проти), це макетний об'єкт . Інакше це заглушка .
Я натрапив на цю цікаву статтю UncleBob The Little Mocker . Він пояснює всю термінологію дуже легко для розуміння, тому її корисно для початківців. Стаття Мартіна Фаулерса важко читається, особливо для початківців, як я.
Стюб допомагає нам провести тест. Як? Він дає значення, які допомагають запустити тест. Ці значення самі по собі не реальні, і ми створили ці значення просто для запуску тесту. Наприклад, ми створюємо HashMap для надання нам значень, подібних до значень у таблиці баз даних. Тому замість прямої взаємодії з базою даних ми взаємодіємо з Hashmap.
Макет - це підроблений об’єкт, який виконує тест. де ми ставимо ствердження.
Дивіться нижче приклад макетів проти заглушок із використанням 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 - це фіктивна реалізація, виконана користувачем статичним способом, тобто в написанні програми Stub. Таким чином, він не може обробляти визначення послуги та динамічний стан, як правило, це робиться в рамках JUnit без використання глузливих фреймворків.
Мок - це також фіктивна реалізація, але її реалізація робиться динамічним способом, використовуючи схеми Mocking як Mockito. Таким чином, ми можемо обробляти умови та визначення послуги як динамічний спосіб, тобто макети можуть створюватися динамічно з коду під час виконання. Тож за допомогою макету ми можемо динамічно реалізовувати Stubs.
Плюс корисних відповідей. Один з найпотужніших моментів використання Mocks, ніж Subs
Якщо співавтор [від якого залежить головний код] не знаходиться під нашим контролем (наприклад, від сторонньої бібліотеки),
в цьому випадку заглушку важче написати, а не знущатися .
У своїй відповіді я використав приклади пітона, щоб проілюструвати відмінності.
Stub - Stubbing - це техніка розробки програмного забезпечення, яка використовується для впровадження методів занять на початку життєвого циклу розвитку. Вони зазвичай використовуються як заповнювачі для реалізації відомого інтерфейсу, де інтерфейс доопрацьований або відомий, але реалізація ще не відома або завершена. Ви починаєте з заглушок, що просто означає, що ви лише записуєте визначення функції вниз і залишаєте фактичний код на потім. Перевага полягає в тому, що ви не забудете методи і можете продовжувати думати про свій дизайн, бачачи його в коді. Ви також можете повернути заглушці статичну відповідь, щоб відповідь могла бути використана іншими частинами коду негайно. Об'єкти Stub надають дійсну відповідь, але це статично незалежно від того, який вхід ви передасте, ви завжди отримаєте однакову відповідь:
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 і затверджує параметр, з якого він викликався. Ви можете використовувати макет з об'єктами не тільки функціями, як показано тут, і ви також можете повернути значення, щоб макетний об'єкт міг бути використаний для заміни заглушки для тестування.
Детальніше на unittest.mock , зверніть увагу на макет python 2.x не включений у 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, і це все. Це означає, що як клас заглушки, так і всі прилади для поведінки, необхідні під час наших тестів, повинні бути чітко визначені.
Наприклад, для stub 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;
}
}
Інший сценарій: якби у мене був метод побічних ефектів (немає повернення) і я перевірив би, що цей метод викликався, я, мабуть, мав би додати булевий або лічильник у класі заглушки, щоб підрахувати, скільки разів був викликаний метод.
Висновок
Заглушка часто вимагає великих накладних витрат / код для написання вашої одиниці тесту. Що макет запобігає завдяки наданню запису / перевірки функцій поза коробкою.
Ось чому в наш час підхід до заглушки рідко застосовується на практиці з появою чудових бібліотек.
Про статтю Мартіна Фаулера: Я не вважаю програмістом-макетом, коли я використовую макети і уникаю заглушок.
Але я використовую макет, коли це дійсно потрібно (дратівливі залежності), і я віддаю перевагу тестовим нарізанням та міні-інтеграційним тестам, коли я тестую клас із залежностями, які знущаються над головою.