Czytałem różne artykuły o kpinach i karczowaniu w testach, w tym kpiny Martina Fowlera , ale nie rozumiem różnicy.
Czytałem różne artykuły o kpinach i karczowaniu w testach, w tym kpiny Martina Fowlera , ale nie rozumiem różnicy.
Odpowiedzi:
Kikut
Uważam, że największym rozróżnieniem jest to, że napisałeś już odcinek ze z góry określonym zachowaniem. Tak więc miałbyś klasę, która implementuje zależność (najprawdopodobniej klasę abstrakcyjną lub interfejs), którą sfałszujesz do celów testowych, a metody zostałyby po prostu zgaszone z ustalonymi odpowiedziami. Nie zrobiliby nic wymyślnego, a ty już napisałeś dla niego kod pośredni poza testem.
Drwić
Kpina jest czymś, co w ramach testu musisz skonfigurować zgodnie ze swoimi oczekiwaniami. Makieta nie jest konfigurowana w określony sposób, więc masz kod, który robi to w teście. Próbki są w pewien sposób określane w czasie wykonywania, ponieważ kod określający oczekiwania musi zostać uruchomiony, zanim cokolwiek zrobi.
Różnica między Mockami a Stubami
Testy pisane próbnymi initialize -> set expectations -> exercise -> verify
próbami zwykle przebiegają według wzoru. Podczas gdy wstępnie napisany kod będzie następował po initialize -> exercise -> verify
.
Podobieństwo między próbkami i skrótami
Celem obu jest wyeliminowanie testowania wszystkich zależności klasy lub funkcji, aby twoje testy były bardziej skoncentrowane i prostsze w tym, co próbują udowodnić.
Istnieje kilka definicji obiektów, które nie są rzeczywiste. Ogólny termin to test podwójny . Termin ten obejmuje: manekin , fałszywy , skrótowy , próbny .
Zgodnie z artykułem Martina Fowlera :
- Atrapy są przekazywane, ale nigdy nie są używane. Zwykle służą one tylko do wypełniania list parametrów.
- Fałszywe obiekty faktycznie mają działające implementacje, ale zwykle wymagają skrótu, który sprawia, że nie nadają się do produkcji (dobrym przykładem jest baza danych w pamięci).
- Stuby zapewniają konserwowane odpowiedzi na wywołania wykonane podczas testu, zwykle nie reagując wcale na nic poza tym, co jest zaprogramowane na test. Stuby mogą również rejestrować informacje o połączeniach, takie jak kod pośredniczący bramy e-mail, który zapamiętuje wiadomości, które „wysłał”, a może tylko liczbę wiadomości, które „wysłał”.
- Mocks są tym, co mówimy o tutaj: Przedmioty z zaprogramowanymi oczekiwań stanowiących specyfikację połączeń oczekuje oni otrzymać.
Egzaminy próbne a stadniny = testy behawioralne a testy stanowe
Zgodnie z zasadą testu tylko jedna rzecz na test , w jednym teście może znajdować się kilka kodów pośredniczących, ale generalnie jest tylko jedna próbka.
Testuj cykl życia za pomocą kodów pośredniczących:
Cykl życia testu z próbami:
Testy próbne i pośredniczące dają odpowiedź na pytanie: Jaki jest wynik?
Testy z próbami są również zainteresowane: Jak uzyskano wynik?
Kikut to prosty fałszywy obiekt. Po prostu upewnia się, że test działa płynnie.
Makieta to mądrzejszy skrót. Sprawdzasz, czy test przechodzi przez to.
Oto opis każdego z nich wraz z próbką ze świata rzeczywistego.
Atrapa - po prostu fałszywe wartości, aby spełnić API
.
Przykład : Jeśli testujesz metodę klasy, która wymaga wielu obowiązkowych parametrów w konstruktorze, które nie mają wpływu na twój test, możesz utworzyć fikcyjne obiekty w celu utworzenia nowych instancji klasy.
Fake - utwórz testową implementację klasy, która może zależeć od jakiejś zewnętrznej infrastruktury. (Dobrą praktyką jest to, że test jednostkowy NIE wchodzi w interakcje z zewnętrzną infrastrukturą).
Przykład : Utwórz fałszywą implementację dostępu do bazy danych, zastąp ją
in-memory
kolekcją.
Stub - zastępuj metody zwracające wartości zakodowane, zwane także state-based
.
Przykład : Twoja klasa testowa zależy od metody
Calculate()
trwającej 5 minut. Zamiast czekać 5 minut, możesz zastąpić jego prawdziwą implementację kodem pośredniczącym, który zwraca zakodowane wartości; zajmuje tylko niewielką część czasu.
Mock - bardzo podobny, Stub
ale interaction-based
raczej niż oparty na stanie. Oznacza to, że nie oczekujesz, Mock
że zwróci jakąś wartość, ale założysz, że tworzona jest określona kolejność wywołań metod.
Przykład: Testujesz klasę rejestracji użytkownika. Po zadzwonieniu
Save
powinien zadzwonićSendConfirmationEmail
.
Stubs
i Mocks
są w rzeczywistości podtypami Mock
, oba zamieniają rzeczywistą implementację na implementację testową, ale z różnych, konkretnych powodów.
W kursieodeschool.com , Rails Testing for Zombies , podają następującą definicję terminów:
Kikut
Do zamiany metody na kod, który zwraca określony wynik.
Drwić
Kod pośredniczący z twierdzeniem, że metoda jest wywoływana.
Tak więc, jak Sean Copenhaver opisał w swojej odpowiedzi, różnica polega na tym, że drwiny ustalają oczekiwania (tj. Robią twierdzenia na temat tego, czy i jak zostaną wywołane).
Stuby nie zawodzą twoich testów, próbuj.
Myślę, że najprostszą i jaśniejszą odpowiedź na to pytanie udziela Roy Osherove w swojej książce The Art of Unit Testing (strona 85)
Najłatwiejszym sposobem na stwierdzenie, że mamy do czynienia z kodem pośredniczącym, jest zauważenie, że kod ten nigdy nie może zawieść testu. Twierdzi, że zastosowania testowe są zawsze w stosunku do testowanej klasy.
Z drugiej strony test użyje próbnego obiektu do sprawdzenia, czy test się nie powiódł. [...]
Ponownie, próbny obiekt to obiekt, którego używamy do sprawdzania, czy test się nie powiódł.
Oznacza to, że jeśli robisz twierdzenia przeciwko fałszywemu, oznacza to, że używasz fałszywego jako makiety, jeśli używasz fałszywego tylko w celu uruchomienia testu bez potwierdzenia, używasz fałszywego jako kodu pośredniczącego.
Czytając wszystkie powyższe wyjaśnienia, postaram się zagęścić:
Mock to po prostu testowanie zachowania, upewniające się, że wywoływane są określone metody. Stub to wersja testowa (per se) konkretnego obiektu.
Co masz na myśli po Apple?
Jeśli porównasz to do debugowania:
Stub przypomina upewnienie się, że metoda zwraca poprawną wartość
Mock jest jak wejście do metody i upewnienie się, że wszystko w środku jest poprawne przed zwróceniem poprawnej wartości.
Użycie modelu mentalnego naprawdę pomogło mi to zrozumieć, zamiast wszystkich wyjaśnień i artykułów, które nie do końca się „zagłębiły”.
Wyobraź sobie, że twoje dziecko ma szklany talerz na stole i zaczyna się nim bawić. Teraz boisz się, że się zepsuje. Dajesz mu zamiast tego plastikową płytkę. To byłby Mock (to samo zachowanie, ten sam interfejs, „bardziej miękka” implementacja).
Teraz powiedz, że nie masz plastikowego zamiennika, więc wyjaśnisz „Jeśli będziesz dalej z nim bawił, pęknie!”. To jest Stub , wcześniej podałeś wcześniej zdefiniowany stan.
Dummy byłoby widelec nawet nie używać ... I Spy może być coś podobnego zapewniając taką samą wyjaśnienie już używany, który pracował.
Myślę, że najważniejszą różnicą między nimi są ich intencje.
Pozwól, że spróbuję wyjaśnić to w DLACZEGO pośredniku kontra DLACZEGO udawanie
Załóżmy, że piszę kod testowy dla publicznego kontrolera osi czasu mojego klienta Mac twitter
Oto przykładowy kod testowy
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
Pisząc próbną, odkrywasz relację współpracy między obiektami, sprawdzając, czy spełnione są oczekiwania, a kod pośredni symuluje tylko zachowanie obiektu.
Proponuję przeczytać ten artykuł, jeśli chcesz dowiedzieć się więcej o próbach: http://jmock.org/oopsla2004.pdf
Aby być bardzo jasnym i praktycznym:
Stub: klasa lub obiekt, który implementuje metody sfałszowanej klasy / obiektu i zwraca zawsze to, co chcesz.
Przykład w JavaScript:
var Stub = {
method_a: function(param_a, param_b){
return 'This is an static result';
}
}
Mock: To samo z kodem pośredniczącym, ale dodaje pewną logikę, która „weryfikuje”, gdy wywoływana jest metoda, dzięki czemu masz pewność, że niektóre implementacje wywołują tę metodę.
Jak mówi @mLevan, wyobraź sobie jako przykład, że testujesz klasę rejestracji użytkownika. Po wywołaniu Save powinien wywołać SendConfirmationEmail.
Bardzo głupi kod Przykład:
var Mock = {
calls: {
method_a: 0
}
method_a: function(param_a, param_b){
this.method_a++;
console.log('Mock.method_a its been called!');
}
}
Ten slajd bardzo dobrze wyjaśnia główne różnice.
* Z CSE 403 Wykład 16, University of Washington (slajd stworzony przez „Marty Stepp”)
Podoba mi się wyjaśnienie Roy Osherove [link do filmu] .
Każda utworzona klasa lub obiekt jest fałszywy. Jest to próbka, jeśli zweryfikujesz przeciwko niej połączenia. W przeciwnym razie jest to zalążek.
zobaczmy Test Doubles:
Stub : Stub to obiekt, który przechowuje predefiniowane dane i używa go do odbierania połączeń podczas testów. Na przykład : obiekt, który musi pobrać niektóre dane z bazy danych, aby odpowiedzieć na wywołanie metody.
Mocks : Mocks to obiekty, które rejestrują odbierane połączenia. W zapewnieniu testowym możemy zweryfikować na próbkach, że wszystkie oczekiwane działania zostały wykonane. Takie jak : funkcja wywołująca usługę wysyłania wiadomości e-mail. po więcej sprawdź to .
Fałszywy to ogólny termin, który może być używany do opisania albo niedopałek lub atrapa obiektu (odręcznie lub w inny sposób), ponieważ zarówno wygląd jak prawdziwy przedmiot.
To, czy podróbka jest kikutem, czy próbą, zależy od tego, jak zostanie wykorzystany w bieżącym teście. Jeśli jest używany do sprawdzania interakcji (potwierdzony przeciwko), jest to próbny obiekt. W przeciwnym razie jest to zalążek.
Podróbki zapewniają sprawne działanie testu. Oznacza to, że czytelnik twojego przyszłego testu zrozumie, jakie będzie zachowanie fałszywego obiektu, bez konieczności czytania jego kodu źródłowego (bez konieczności polegania na zasobach zewnętrznych).
Co oznacza płynny przebieg testu?
Na przykład w poniższym kodzie:
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");
}
}
}
Chcesz przetestować metodę mailService.SendEMail () , aby to zrobić, musisz zasymulować wyjątek w swojej metodzie testowej, więc wystarczy utworzyć klasę Fake Stub errorService, aby zasymulować ten wynik, a następnie kod testowy będzie mógł przetestować Metoda mailService.SendEMail (). Jak widzisz, musisz zasymulować wynik pochodzący z innej klasy ErrorService klasy zależności zewnętrznych.
Bezpośrednio z gazety Mock Role, a nie Objects , twórcy jMock:
Identyfikatory są fałszywymi implementacjami kodu produkcyjnego, które zwracają wyniki w puszkach. Obiekty próbne działają jak kody pośredniczące, ale zawierają także twierdzenia w celu instrumentowania interakcji obiektu docelowego z jego sąsiadami.
Główne różnice to:
Podsumowując, jednocześnie starając się rozproszyć zamieszanie związane z tytułem artykułu Fowlera : kpiny to odcinki, ale nie tylko odcinki .
Czytałem The Art of Unit Testing i natknąłem się na następującą definicję:
Fałszywy to ogólny termin, który może być używany do opisania albo niedopałek lub atrapa obiektu (odręcznie lub w inny sposób), ponieważ zarówno wygląd jak prawdziwy przedmiot. To, czy podróbka jest kikutem, czy próbą, zależy od tego, jak zostanie wykorzystany w bieżącym teście. jeśli jest używany do sprawdzenia interakcji (potwierdzony), jest to obiekt próbny . W przeciwnym razie jest to zalążek .
Natrafiłem na ten interesujący artykuł autorstwa UncleBob The Little Mocker . Wyjaśnia całą terminologię w bardzo łatwy do zrozumienia sposób, więc jest przydatny dla początkujących. Artykuł Martina Fowlersa jest trudny do przeczytania, szczególnie dla początkujących, takich jak ja.
Stub pomaga nam przeprowadzić test. W jaki sposób? Podaje wartości, które pomagają uruchomić test. Te wartości same w sobie nie są rzeczywiste i stworzyliśmy je tylko w celu uruchomienia testu. Na przykład tworzymy HashMap, aby dać nam wartości, które są podobne do wartości w tabeli bazy danych. Zamiast bezpośrednio wchodzić w interakcję z bazą danych, wchodzimy w interakcję z Hashmap.
Mock to fałszywy obiekt, który uruchamia test. gdzie kładziemy nacisk.
Zobacz poniżej przykład makiety vs odcinki przy użyciu frameworka C # i Moq. Moq nie ma specjalnego słowa kluczowego dla kodu Stub, ale możesz także użyć obiektu Mock do utworzenia kodów pośredniczących.
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";
}
}
}
Punkt testowy testu pośredniego i próbnego:
Stub to fałszywa implementacja wykonywana przez użytkownika w sposób statyczny, tzn. W Stubie piszącym kod implementacyjny. Więc nie może obsłużyć definicji usługi i warunku dynamicznego. Zwykle odbywa się to w frameworku JUnit bez użycia frameworku.
Mock jest również implementacją fikcyjną, ale jego implementacja została wykonana dynamicznie przy użyciu frameworków takich jak Mockito. Możemy więc traktować definicję warunków i usług w sposób dynamiczny, tzn. Symulacje można tworzyć dynamicznie z kodu w czasie wykonywania. Tak więc używając makiety możemy dynamicznie implementować Stuby.
Plus przydatne odpowiedzi, jeden z najpotężniejszych punktów korzystania z Mocków niż subskrybcji
Jeśli współpracownik [od którego zależy główny kod] nie jest pod naszą kontrolą (np. Z biblioteki innej firmy),
W tym przypadku stub jest trudniejszy do napisania niż próbowania .
Użyłem przykładów python w mojej odpowiedzi, aby zilustrować różnice.
Stub - Stubbing to technika opracowywania oprogramowania służąca do wdrażania metod zajęć na wczesnym etapie cyklu rozwojowego. Są one powszechnie stosowane jako symbole zastępcze dla implementacji znanego interfejsu, gdzie interfejs jest sfinalizowany lub znany, ale implementacja nie jest jeszcze znana ani sfinalizowana. Zaczynasz od kodów pośredniczących, co oznacza po prostu, że zapisujesz tylko definicję funkcji i pozostawiasz kod na później. Zaletą jest to, że nie zapomnisz metod i możesz nadal myśleć o swoim projekcie, widząc go w kodzie. Możesz również ustawić swój kod zwrotny na odpowiedź statyczną, dzięki czemu odpowiedź może zostać natychmiast wykorzystana przez inne części kodu. Obiekty wejściowe zapewniają prawidłową odpowiedź, ale są statyczne bez względu na przekazane dane wejściowe, zawsze otrzymasz tę samą odpowiedź:
class Foo(object):
def bar1(self):
pass
def bar2(self):
#or ...
raise NotImplementedError
def bar3(self):
#or return dummy data
return "Dummy Data"
Drwić obiekty są wykorzystywane w próbnych testach oni potwierdzić, że niektóre metody są nazywane na tych obiektach. Sztuczne obiekty to symulowane obiekty, które naśladują zachowanie rzeczywistych obiektów w kontrolowany sposób. Zazwyczaj tworzy się próbny obiekt w celu przetestowania zachowania jakiegoś innego obiektu. Próbki pozwalają nam symulować zasoby, które są niedostępne lub zbyt nieporęczne do testów jednostkowych.
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()
Jest to bardzo prosty przykład, który po prostu uruchamia rm i potwierdza parametr, za pomocą którego został wywołany. Możesz użyć makiety z obiektami, nie tylko funkcjami, jak pokazano tutaj, a także możesz zwrócić wartość, dzięki czemu można użyć makiety do zastąpienia kodu pośredniczącego do testowania.
Więcej na unittest.mock , uwaga w 2.x mock nie jest zawarta w unittest, ale jest modułem do pobrania, który można pobrać przez pip (mock install pip).
Przeczytałem także „The Art of Unit Testing” Roy'a Osherove i myślę, że byłoby wspaniale, gdyby podobna książka została napisana przy użyciu Pythona i przykładów Pythona. Jeśli ktoś wie o takiej książce, udostępnij ją. Twoje zdrowie :)
Stub to fałszywy obiekt zbudowany do celów testowych. Próbny to kod pośredniczący, który rejestruje, czy oczekiwane połączenia zostały skutecznie wykonane.
Kod pośredniczący to pusta funkcja, która służy do unikania nieobsługiwanych wyjątków podczas testów:
function foo(){}
Mock to sztuczna funkcja używana w celu uniknięcia zależności systemu operacyjnego, środowiska lub sprzętu podczas testów:
function foo(bar){ window = this; return window.toString(bar); }
Pod względem twierdzeń i stanu:
Bibliografia
tam jest wiele prawidłowych odpowiedzi, ale myślę, że warto wspomnieć o tym formularzu wuj Bob: https://8thlight.com/blog/uncle-bob/2014/05/14/TheLittleMocker.html
najlepsze wyjaśnienie z przykładami!
Makieta jest zarówno przedmiotem technicznym, jak i funkcjonalnym .
Kpina jest techniczna . Jest on rzeczywiście tworzony przez fałszywą bibliotekę (z tego znane są EasyMock, JMockit i ostatnio Mockito) dzięki generowaniu kodu bajtowego .
Implementacja próbna jest generowana w taki sposób, że możemy ją instrumentować , aby zwracała określoną wartość po wywołaniu metody, ale także kilka innych rzeczy, takich jak sprawdzenie, czy wywołano próbną metodę z określonymi parametrami (ścisłe sprawdzenie) lub dowolnymi parametrami ( brak ścisłej kontroli).
Tworzenie makiety:
@Mock Foo fooMock
Nagrywanie zachowania:
when(fooMock.hello()).thenReturn("hello you!");
Weryfikacja wywołania:
verify(fooMock).hello()
To oczywiście nie jest naturalny sposób na tworzenie / nadpisywanie klasy / zachowania Foo. Dlatego odnoszę się do aspektu technicznego.
Ale makieta jest również funkcjonalna, ponieważ jest instancją klasy, którą musimy odizolować od SUT. A z zarejestrowanymi zachowaniami, moglibyśmy użyć go w SUT w taki sam sposób, jak zrobilibyśmy go z kodem pośredniczącym.
Kod pośredniczący to tylko obiekt funkcjonalny : jest to instancja klasy, którą musimy odizolować od SUT i to wszystko. Oznacza to, że zarówno klasa pośrednicząca, jak i wszystkie urządzenia zachowań potrzebne podczas naszych testów jednostkowych muszą być wyraźnie zdefiniowane.
Na przykład kod pośredniczący hello()
musiałby podklasować Foo
klasę (lub implementować interfejs, który on ma) i przesłonić hello()
:
public class HelloStub extends Hello{
public String hello {
return "hello you!";
}
}
Jeśli inny scenariusz testowy wymaga zwrotu innej wartości, prawdopodobnie będziemy musieli zdefiniować ogólny sposób ustawiania zwrotu:
public class HelloStub extends Hello{
public HelloStub(String helloReturn){
this.helloReturn = helloReturn;
}
public String hello {
return helloReturn;
}
}
Inny scenariusz: gdybym miał metodę skutków ubocznych (bez powrotu) i sprawdziłbym, czy ta metoda została wywołana, prawdopodobnie powinienem dodać wartość logiczną lub licznik w klasie stub, aby policzyć, ile razy metoda została wywołana.
Wniosek
Kod pośredniczący wymaga często dużego obciążenia / kodu do napisania do testu jednostkowego. Jakiej kpiny zapobiega dzięki zapewnieniu funkcji nagrywania / weryfikacji od razu po wyjęciu z pudełka.
Dlatego obecnie podejście stub jest rzadko stosowane w praktyce wraz z pojawieniem się doskonałych fałszywych bibliotek.
O artykule Martina Fowlera: Nie uważam się za programistę „mockist”, kiedy używam makiet i unikam kodów pośredniczących.
Ale używam makiety, gdy jest to naprawdę wymagane (irytujące zależności) i preferuję testy krojenia i testy mini-integracji, gdy testuję klasę z zależnościami, których kpowanie byłoby narzutem.