Яка різниця між підробкою, знущанням та заглушкою?


706

Я знаю, як я використовую ці терміни, але мені цікаво, чи є прийняті визначення для підробки , глузування та заглушки для одиничних тестів? Як ви їх визначаєте для своїх тестів? Опишіть ситуації, коли ви можете використовувати кожну.

Ось як я їх використовую:

Підробка : клас, який реалізує інтерфейс, але містить фіксовані дані та відсутність логіки. Просто повертає "хороші" або "погані" дані залежно від реалізації.

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

Stub : Як і макетний клас, за винятком того, що він не дає можливості перевірити, що методи були викликані / не викликані.

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


6
Ну, ви в основному все це сказали у своєму "питанні" :) Я думаю, що це досить добре прийняті визначення цих термінів
Еран Гальперін

2
Визначення Вікіпедії підробленого відрізняється від цього, стверджуючи, що підробка "використовується як простіша реалізація, наприклад, використання тестової бази даних в тестах замість того, щоб здійснювати реальний доступ до бази даних)" Див. En.wikipedia.org/wiki/Test_double
zumalifeguard

2
Я багато чого навчився на наступному ресурсі, із чудовим поясненням Роберта К. Мартіна (дядька Боб): Маленький глузник у блозі «Чистий код» . Це пояснює відмінності між і тонкощами манекенів, тестових пар, стрибків, шпигунів, (справжніх) макетів і підробок. Він також згадує Мартіна Фаулера, і це пояснює трохи історії тестування програмного забезпечення.
Ерік

testing.googleblog.com/2013/07/… (короткий підсумок на одній сторінці).
ShreevatsaR

Ось мій погляд, щоб пояснити це: Тест парних питань: фальсифікати, заглушки та знущання (повідомлення в блозі з прикладами)
michal-lipki

Відповіді:


549

Ви можете отримати інформацію:

Від Мартіна Фаулера про Мока і Стуба

Підроблені об'єкти насправді мають робочі реалізації, але зазвичай вони мають деякий ярлик, який робить їх непридатними для виробництва

Заглушки надають консервовані відповіді на дзвінки, здійснені під час тесту, зазвичай взагалі не відповідають на те, що передбачено для тесту. Заглушки можуть також записувати інформацію про дзвінки, наприклад, заглушку шлюзу електронної пошти, яка запам’ятовує повідомлення, які вона надіслала, або, можливо, лише скільки повідомлень вона надіслала.

Знущання - це те, про що ми говоримо тут: об’єкти, заздалегідь запрограмовані на очікування, які формують специфікацію дзвінків, які вони очікують приймати.

Від xunitpattern :

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

Заглушка : Ця реалізація налаштована для відповіді на дзвінки з SUT зі значеннями (або винятками), які будуть використовувати Неперевірений код (див. Виправні помилки на сторінці X) в SUT. Ключовою ознакою використання тестового заводу є наявність неперевіреного коду, спричиненого неможливістю контролювати непрямі входи SUT

Макетний об’єкт, який реалізує той самий інтерфейс, що і об'єкт, від якого залежить SUT (System Under Test). Ми можемо використовувати Mock Object як пункт спостереження, коли нам потрібно зробити перевірку поведінки, щоб уникнути неперевіреної вимоги (див. Виправні помилки на сторінці X), викликаної неможливістю спостерігати побічні ефекти методів виклику на SUT.

Особисто

Я намагаюся спростити, використовуючи: Mock and Stub. Я використовую Mock, коли це об'єкт, який повертає значення, встановлене для тестованого класу. Я використовую Stub для імітації інтерфейсу або класу «Анотація», який потрібно перевірити. Насправді, це неважливо, як ви це називаєте, це всі класи, які не використовуються у виробництві, і використовуються як корисні класи для тестування.


9
Мені здається, що визначення для Stub і Fake перетворено в цитаті xUnitPattern порівняно з цитатою Мартіна Фаулера. Крім того, що визначення Мартіна Фаулера "Стуб і фейк" є зворотними порівняно з визначеннями в первинному питанні тванфоссона. Насправді чи є загальноприйняті визначення цих двох термінів чи це просто залежить від того, з ким ви говорите?
Саймон Тевсі

3
+1 для "Я намагаюся спростити, використовуючи: Mock and Stub". Це чудова ідея!
Бред Купіт

4
Не бачу, як чудово використовувати ідею Mock and Stub. Кожен тестовий двійник має свої цілі, а отже, і використання.
Гектор Ордонез

1
Я не бачу різниці між фейком і макетом у визначенні MF.
IdontCare AboutReputationPoints

2
@MusuNaji: У визначенні MF немає ніяких "очікувань" щодо розмови для підробки, окрім того, що вона має реалізацію для свого інтерфейсу. З іншого боку, Макет буде оскаржений (чи називався цей метод?).
dbalakirev

205

Stub - об’єкт, що надає заздалегідь відповіді на виклики методів.

Макет - об’єкт, на який ви ставите очікування.

Підробка - об'єкт з обмеженими можливостями (для цілей тестування), наприклад, підроблена веб-служба.

Test Double - загальний термін для заглушок, макетів та підробок. Але неофіційно ви часто чуєте, як люди просто називають їх глузуючими.


4
Чи може хтось пояснити та визначити мені, що таке "відповідь на консерви" в цьому контексті?
MasterMastic

14
Явне значення, а не значення, яке обчислюється.
Майк

Нарешті! Деякі визначення я можу зрозуміти! Виходячи з цих визначень, googletest (gtest) / googlemock (gmock) дозволяє об'єктам, що висміюються, також бути заглушками, оскільки ви можете створювати EXPECT_CALL()s за допомогою глузливого методу, який примушує певні виходи на основі певних входів, використовуючи тип .WillOnce(Invoke(my_func_or_lambda_func))(або з .WillRepeatedly()) синтаксис, приєднаний до EXPECT_CALL(). Деякі приклади використання Invoke()можна побачити в іншому контексті внизу моєї довгої відповіді тут: stackoverflow.com/a/60905880/4561887 .
Габріель Степлес

Gmock документація на Invoke()тут: github.com/google/googletest/blob/master/googlemock/docs / ... . У будь-якому випадку, висновок такий: Google mock (gmock) дозволяє легко створювати і макети, і заглушки , хоча більшість макетів не є заглушками.
Габріель Степлес

Макети - це суперкомплект Стубів, вони все ще можуть повернути заздалегідь задані відповіді, але також дозволяють розробнику задавати очікування. Деякі бібліотеки ІМО там розмивають лінії всіх манекенів тестування.
Лука

94

Я здивований, що це питання існує вже давно, і ніхто досі не дав відповіді за мотивами "Мистецтва тестування одиниць" Роя Ошерова .

У "3.1 Представлення заглушок" визначається заглушка як:

Заглушка - це керована заміна існуючої залежності (або співпрацівника) в системі. Використовуючи заглушку, ви можете протестувати свій код, не маючи прямого відношення до залежності.

І визначає різницю між заглушками та макетами як:

Головне, що потрібно пам’ятати про макети проти заглушок, це те, що макети - це як заглушки, але ви стверджуєте проти макетного об’єкта, тоді як ви не стверджуєте проти заглушки.

Підробка - це лише назва, яке використовується як для заглушок, так і для макетів. Наприклад, коли вам не байдуже розрізнення заглушок і макетів.

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

  • Коли ваш тест перевіряє значення в тестовому класі або насправді в іншому місці, окрім підробки, підробка використовувалася як заглушка. Він просто надав значення для тестуваного класу для використання, або безпосередньо через значення, що повертаються дзвінками на нього, або опосередковано через спричинення побічних ефектів (у деякому стані) в результаті дзвінків на нього.
  • Коли ваш тест перевіряє значення підробки, він був використаний як макет.

Приклад тесту, коли клас 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, роблячи це підробленим макетом.

Тепер, звичайно, ці приклади дуже надумані, але я бачу велику заслугу в цьому розрізненні. Це дає вам знати про те, як ви протестуєте свої речі та де залежність вашого тесту.

Я згоден з цим Ошеровим

З точки зору чистої ремонтопридатності, у моїх тестах використання макетів створює більше проблем, ніж їх використання. Це був мій досвід, але я завжди дізнаюся щось нове.

Стверджувати проти фальшивки - це те, чого ви дійсно хочете уникати, оскільки це робить ваші тести сильно залежними від впровадження класу, який зовсім не тестується. Що означає, що тести для класу ActualClassUnderTestможуть почати ламати, тому що реалізація ClassUsedAsMockзмінилася. І це передає мені неприємний запах. Тести на ActualClassUnderTestперевагу бажано перервати лише після ActualClassUnderTestзміни.

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

Для задоволення від читання того, чому слід уникати знущань, як визначено тут, google для "недоброзичливого мокістичного класициста". Ви знайдете безліч думок.


31

Як згадується у голосуванні у відповідь, Мартін Фаулер обговорює ці відмінності у Mocks Aren't Stubs , і, зокрема, у підзаголовку The Difference Between Mocks and Stubs , тому обов'язково прочитайте цю статтю.

Замість того, щоб концентруватися на тому, як ці речі відрізняються, я думаю, що більш приємно зосередитися на тому, чому це різні поняття. Кожна існує з іншою метою.

Підробки

Підробка є реалізацією , яка поводиться «природно», але не «реальний». Це нечіткі поняття, і тому різні люди по-різному розуміють, що робить речі підробкою.

Одним із прикладів підробки є база даних в пам'яті (наприклад, використання sqlite з :memory:магазином). Ви ніколи не використовуєте це для виробництва (оскільки дані не зберігаються), але він цілком адекватний як база даних для використання в тестовому середовищі. Це також набагато легше, ніж "справжня" база даних.

В якості іншого прикладу, можливо, ви використовуєте якийсь сховище об'єктів (наприклад, Amazon S3) у виробництві, але в тесті ви можете просто зберегти об'єкти у файли на диску; то реалізація "збереження на диску" була б підробкою. (Або ви навіть можете підробити операцію "збереження на диску", використовуючи замість цього файлову систему в пам'яті.)

В якості третього прикладу уявіть об’єкт, що забезпечує API кешу; об’єкт, який реалізує правильний інтерфейс, але просто не виконує кешування взагалі, але завжди повертає пропуск кеша, був би своєрідною підробкою.

Мета підробки - не впливати на поведінку тестованої системи , а скоріше спростити виконання тесту (шляхом усунення зайвих або важких залежностей).

Заглушки

Заглушка є реалізація , яка поводиться «неприродно». Заздалегідь налаштовано (зазвичай за допомогою тестової установки), щоб відповідати на конкретні входи із конкретними виходами.

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

Заглушка зазвичай реалізується лише для того, щоб відповідати на точні взаємодії, на які ви сказали. Але ключовою особливістю, яка робить щось заглушкою, є її мета : заглушка - все про налаштування вашого тестового випадку.

Знущаються

Макет схожий на заглушку, але з перевіркою доданою. Мета макета, щоб зробити твердження про те , як ваша система випробовуваної взаємодіяла із залежністю .

Наприклад, якщо ви пишете тест для системи, яка завантажує файли на веб-сайт, ви можете створити макет, який приймає файл, і який ви можете використовувати, щоб стверджувати, що завантажений файл був правильним. Або, у меншому масштабі, звичайно використовувати макет об’єкта для перевірки того, що система, що перевіряється, викликає конкретні методи знученого об'єкта.

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

Тестовий пар

Підробки, заглушки та глузування належать до категорії тестових пар . Тестовий подвійний - це будь-який об'єкт або система, яку ви використовуєте в тесті замість чогось іншого. Більшість автоматизованих тестувань програмного забезпечення передбачає використання тестових пар, тих чи інших. Деякі інші типи парних пар містять фіктивні значення , шпигуни та чорнобривці вводу / виводу .


11

Щоб проілюструвати використання заглушок і макетів, я хотів би також навести приклад, заснований на творі Роя Ошерова " Мистецтво тестування одиниць ".

Уявіть, у нас є додаток 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);
 }
}

9

річ, яку ви стверджуєте на ньому, називається макетним об'єктом, і все інше, що тільки допомогло пробному запуску, - це заглушка .


1
в той час як інші відповіді мають великі деталі і справді хороші. це робить це таким зрозумілим і легким, щоб змінити значення, важко не подати заяву. гж!
Маріо Гарсія

6

Це питання зробити тести виразними. Я встановлюю очікування на Mock, якщо хочу, щоб тест описував взаємозв'язок між двома об'єктами. Я заглублюю повернені значення, якщо я налаштовую підтримуючий об’єкт, щоб привернути мене до цікавої поведінки в тесті.


6

Якщо ви знайомі з Arrange-Act-Assert, то один із способів пояснення різниці між заглушкою та макетом, який може бути корисним для вас, полягає в тому, що заглушки належать до розділу упорядкування, як вони для упорядкування вхідного стану, а макети належать до розділ ствердження, як вони є для затвердження результатів проти.

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


3

Стюб, фальшивка та макети мають різні значення в різних джерелах. Я пропоную вам ввести внутрішні терміни своєї команди та погодити їх значення.

Я думаю, що важливо розрізняти два підходи: - перевірка поведінки (передбачає підміну поведінки) - перевірка кінцевого стану (передбачає емуляцію поведінки)

В разі помилки розглянути можливість надсилання електронної пошти. Виконуючи перевірку поведінки - ви перевіряєте, Sendчи IEmailSenderбув виконаний метод один раз. І вам потрібно наслідувати результат повернення цього методу, повернути Id відправленого повідомлення. Отже, ви кажете: "Я очікую, що Sendвін буде викликаний. І я просто поверну фіктивний (або випадковий) Id для будь-якого дзвінка" . Це перевірка поведінки: emailSender.Expect(es=>es.Send(anyThing)).Return((subject,body) => "dummyId")

Виконуючи перевірку стану, вам потрібно буде створити TestEmailSenderці програми IEmailSender. І Sendметод реалізації - зберігаючи вхід до деякої структури даних, яка буде використовуватися для подальшої перевірки стану, наприклад масиву деяких об'єктів, SentEmailsа потім перевіряється, ви перевіряєте, чи SentEmailsмістить очікуваний електронний лист. Це перевірка стану: Assert.AreEqual(1, emailSender.SentEmails.Count)

З читання я зрозумів, що перевірку поведінки зазвичай називають глузуванням . А перевірку стану зазвичай називають Stubs або Falks .


Дійсно добре детальне та чітке визначення.
shyam sundar singh tomar

2

заглушка та підробка - це об'єкти, завдяки яким вони можуть змінювати свою реакцію залежно від вхідних параметрів. головна відмінність між ними полягає в тому, що підробка ближче до реальної реалізації, ніж заглушка. Заглушки містять в основному жорсткі відповіді на очікуваний запит. Давайте подивимось приклад:

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. Більшість глумців можуть відстежувати, скільки разів викликали метод, і можуть реагувати на основі цієї інформації. Знущаються, як правило, добре знають контекст кожного дзвінка і можуть реагувати по-різному в різних ситуаціях. Через це макети вимагають певних знань про клас, з якого знущаються. заглушка, як правило, не може відстежувати, скільки разів викликали метод або в якому порядку викликали послідовність методів. Макет виглядає так:

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;
 }

}

1

fake objectце реальна реалізація інтерфейсу (протоколу) або розширення з використанням успадкування або інших підходів, які можна використовувати для створення, - це залежність. Зазвичай він створюється розробником як найпростіше рішення для заміни якоїсь залежності

stub object- це голий об'єкт (0, нуль та методи без логіки) з додатковою та попередньо визначеною (розробником) станом для визначення повернених значень. Зазвичай він створюється рамками

mock objectдуже схожий, stub objectале додатковий стан змінюється під час виконання програми, щоб перевірити, чи щось сталося (метод викликався).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.