J'ai lu divers articles sur les moquages et les moqueries lors des tests, y compris Les moqueries de Martin Fowler , mais je ne comprends toujours pas la différence.
Stub
Je crois que la plus grande distinction est qu'un talon que vous avez déjà écrit avec un comportement prédéterminé. Donc, vous auriez une classe qui implémenterait la dépendance (classe abstraite ou interface le plus probable) que vous simulez à des fins de test et les méthodes seraient simplement écrites avec des réponses set. Ils ne feraient rien d'extraordinaire et vous auriez déjà écrit le code abrégé pour cela en dehors de votre test.
Mock
Une simulation est quelque chose que, dans le cadre de votre test, vous devez définir vos attentes. Une maquette n'est pas configurée d'une manière prédéterminée, vous avez donc un code qui le fait lors de votre test. Les simulacres sont en quelque sorte déterminés au moment de l'exécution, car le code qui définit les attentes doit s'exécuter avant de faire quoi que ce soit.
Différence
Les tests écrits avec des simulacres suivent généralement un modèle initialize -> set expectations -> exercise -> verify
à tester. Alors que le stub pré-écrit suivrait un initialize -> exercise -> verify
.
Similarité
Le but des deux est d'éliminer le test de toutes les dépendances d'une classe ou d'une fonction afin que vos tests soient plus ciblés et plus simples dans ce qu'ils essaient de prouver.
Il existe plusieurs définitions d'objets qui ne sont pas réelles. Le terme général est test double. Ce terme englobe: dummy, fake, stub, mock.
D'après article de Martin Fowler :
- Les objets Dummy sont passés mais ne sont jamais réellement utilisés. Habituellement, ils sont simplement utilisés pour remplir des listes de paramètres.
- Les objets Fake ont des implémentations fonctionnelles, mais prennent généralement un raccourci qui les rend impropres à la production (une base de données en mémoire est un bon exemple).
- Stubs fournit des réponses prédéfinies aux appels passés pendant le test et ne répond généralement pas du tout à des événements autres que ceux programmés pour le test. Les talons peuvent également enregistrer des informations sur les appels, tels qu'un talon de passerelle de messagerie qui se souvient des messages qu'il a "envoyés", ou peut-être uniquement du nombre de messages qu'il "a envoyés".
- Mocks sont ce dont nous parlons ici: des objets préprogrammés avec des attentes qui constituent une spécification des appels qu’ils sont censés recevoir.
Mocks vs Stubs = tests comportementaux vs tests d'état
Selon le principe Tester une seule chose par test, il peut y avoir plusieurs stubs dans un test, mais en général, il n'y a qu'un seul modèle.
Testez le cycle de vie avec des stubs:
Testez le cycle de vie avec des modèles:
Les tests sur les moignons et les moignons donnent une réponse à la question: Quel est le résultat?
Les tests avec des simulacres sont également intéressés par: Comment le résultat a-t-il été obtenu?
Stub est un faux objet simple. Il s'assure simplement que le test se déroule bien.
Mock est plus malin. Vous vérifiez que votre test passe à travers.
Voici une description de chacun suivie d'un échantillon du monde réel.
Dummy - juste des valeurs factices pour satisfaire la API
.
Exemple: Si vous testez une méthode d'une classe nécessitant de nombreux paramètres obligatoires dans un constructeur qui n'a aucun effet sur votre test, vous pouvez créer des objets factices dans le but de: créer de nouvelles instances d'une classe.
Fake - crée une implémentation test d'une classe pouvant dépendre d'une infrastructure externe. (Il est judicieux que votre test unitairePASinteragisse réellement avec une infrastructure externe.)
Exemple: Créez une fausse implémentation pour accéder à une base de données, remplacez-la par la collection
in-memory
.
Stub - substitue les méthodes permettant de renvoyer des valeurs codées en dur, également appelées state-based
.
Exemple: Votre classe de test dépend d'une méthode dont le nom
Calculate()
prend 5 minutes. Plutôt que d’attendre 5 minutes, vous pouvez remplacer sa véritable implémentation par un stub renvoyant des valeurs codées en dur; ne prenant qu'une petite fraction du temps.
Mock - très similaire à Stub
mais interaction-based
plutôt qu'à l'état. Cela signifie que vous n'attendez pas de Mock
qu'il renvoie une valeur, mais supposez que cet ordre spécifique d'appels de méthode est effectué.
Exemple: vous testez une classe d'enregistrement d'utilisateur. Après avoir appelé
Save
, il devrait appelerSendConfirmationEmail
.
Stubs
et Mocks
sont en réalité des sous-types de Mock
, les deux implémentation réelle swap avec implémentation test, mais pour des raisons différentes et spécifiques.
Dans le codeschool.com course, Rails Testing for Zombies , ils donnent cette définition des termes:
Talon
Pour remplacer une méthode par un code qui renvoie un résultat spécifié.
Moquer
Un talon avec une assertion que la méthode est appelée.
Ainsi, comme le décrit Sean Copenhaver dans sa réponse, la différence est que les moqueries définissent des attentes (c'est-à-dire font des affirmations sur le fait de savoir si ou comment elles sont appelées).
Les moignons n'échouent pas vos tests, fake can.
Je pense que la réponse la plus simple et la plus claire sur cette question provient de Roy Osherove dans son livre L'art des tests unitaires (page 85)
La meilleure façon de savoir que nous traitons avec un stub est de remarquer que celui-ci ne peut jamais échouer au test. Les assertions que le test utilise sont toujours contre la classe sous test.
D'autre part, le test utilisera un objet fictif pour vérifier si le fichier test a échoué ou pas. [...]
Encore une fois, l'objet fictif est l'objet que nous utilisons pour voir si le test a échoué ou non.
Cela signifie que si vous faites des assertions contre le faux, cela signifie que vous utilisez le faux comme un simulacre, si vous utilisez le faux uniquement pour exécuter le test sans affirmation, vous utilisez le faux comme un talon.
En lisant toutes les explications ci-dessus, permettez-moi de condenser:
Un simulacre teste simplement son comportement en s'assurant que certaines méthodes s'appellent . Un stub est une version testable (en soi) d'un objet particulier.
Qu'est-ce que tu veux dire d'une manière Apple?
Je pense que la différence la plus importante entre eux est leurs intentions.
Laissez-moi essayer de l'expliquer dans WHY stub vs WHY mock
Supposons que je rédige un code de test pour le contrôleur de la timeline publique de mon client Twitter
Voici le code de l'échantillon de test
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
En écrivant mock, vous découvrez la relation de collaboration entre objets en vérifiant que les attentes sont satisfaites, tandis que stub ne simule que le comportement de l'objet.
Je suggère de lire cet article si vous essayez d’en savoir plus sur mocks: http://jmock.org/oopsla2004.pdf
Si vous le comparez au débogage:
Stub revient à s'assurer qu'une méthode renvoie la valeur correcte
Mock est comme/ entrer dans la méthode et s'assurer que tout ce qu'il contient est correct avant de retourner la valeur correcte.
Pour être très clair et pratique:
Stub: Une classe ou un objet qui implémente les méthodes de la classe/objet à simuler et retourne toujours ce que vous voulez.
Exemple en JavaScript:
var Stub = {
method_a: function(param_a, param_b){
return 'This is an static result';
}
}
Mock: La même chose que pour stub, mais cela ajoute une logique qui "vérifie" quand une méthode est appelée afin que vous puissiez être sûr qu'une implémentation appelle cette méthode.
Comme @mLevan le dit, imaginons à titre d'exemple que vous testez une classe d'enregistrement d'utilisateur. Après avoir appelé Save, il convient d’appeler SendConfirmationEmail.
Un code très stupide Exemple:
var Mock = {
calls: {
method_a: 0
}
method_a: function(param_a, param_b){
this.method_a++;
console.log('Mock.method_a its been called!');
}
}
Utiliser un modèle mental m'a vraiment aidé à comprendre cela, plutôt que toutes les explications et tous les articles, qui n'ont pas vraiment «sombré».
Imaginez que votre enfant ait une assiette en verre sur la table et qu'il commence à jouer avec. Maintenant, vous avez peur que ça casse. Donc, vous lui donnez une assiette en plastique à la place. Ce serait un Mock (même comportement, même interface, implémentation "plus douce").
Maintenant, dites que vous n'avez pas le plastique de remplacement, alors expliquez-vous "Si vous continuez à jouer avec, ça va casser!". C'est un Stub , vous avez fourni un état prédéfini à l'avance.
Un Dummy serait la fourchette qu'il n'a même pas utilisée ... et un Spy pourrait être quelque chose comme fournir la même explication que vous avez déjà utilisée qui a fonctionné. A Fake serait comme mentir qu'un policier viendra s'il continue (pas sûr de la dernière partie :).
J'aime l'explication de Roy Osherove [lien vidéo] .
Chaque classe ou objet créé est un Fake. C'est une maquette si vous vérifiez appelle contre elle. Sinon, c'est un bout.
Un fake est un terme générique qui peut être utilisé pour décrire un objet stub .__ ou un objet fictif (écrit à la main ou autrement), car ils ressemblent tous les deux à l'objet réel
Que ce soit un faux ou un faux dépend de la façon dont il est utilisé dans le test en cours. S'il est utilisé pour vérifier une interaction (affirmée contre), il s'agit d'un objet fictif Sinon, c’est un talon.
Fakes assure le bon déroulement du test. Cela signifie que le lecteur de votre futur test comprendra quel sera le comportement du faux objet, sans avoir besoin de lire son code source (sans avoir besoin de dépendre d'une ressource externe) .
Que signifie test en douceur?
Exemple dans le code ci-dessous:
public void Analyze(string filename)
{
if(filename.Length<8)
{
try
{
errorService.LogError("long file entered named:" + filename);
}
catch (Exception e)
{
mailService.SendEMail("[email protected]", "ErrorOnWebService", "someerror");
}
}
}
Vous souhaitez tester la méthode mailService.SendEMail (). Pour ce faire, vous devez simuler une exception dans votre méthode de test. Il vous suffit donc de créer une classe Fake Stub errorService pour simuler ce résultat. Votre code de test être en mesure de tester la méthode mailService.SendEMail (). Comme vous le voyez, vous devez simuler un résultat provenant d'une autre classe External Dependency ErrorService.
laissez voir les doubles d'essais:
Stub: Stub est un objet qui contient des données prédéfinies et l'utilise pour répondre aux appels lors des tests. Tels que: un objet qui doit extraire des données de la base de données pour répondre à un appel de méthode.
Mocks: les mock sont des objets qui enregistrent les appels qu'ils reçoivent . Dans l'assertion de test, nous pouvons vérifier sur Mock que toutes les actions attendues ont été effectuées. Tels que: une fonctionnalité qui appelle le service d’envoi de courrier électronique ..__ pour plus d’informations, il suffit de vérifier this .
Tout droit sorti du papier Mock Roles, not Objects , par les développeurs de jMock:
Les stubs sont des implémentations factices du code de production qui renvoient canned résultats. Les objets factices agissent comme des talons, mais incluent également des affirmations à instrument les interactions de l'objet cible avec ses voisins.
Ainsi, les principales différences sont les suivantes:
Pour résumer, tout en essayant de dissiper la confusion de l'article de Fowler title: mocks sont des moignons, mais ce ne sont pas seulement des moignons.
Je suis tombé sur cet article intéressant de UncleBob The Little Mocker . Il explique toute la terminologie de manière très facile à comprendre, donc utile pour les débutants. L'article de Martin Fowlers est difficile à lire, spécialement pour les débutants comme moi.
Voir ci-dessous un exemple de simulacres vs de talons utilisant les frameworks C # et Moq. Moq n'a pas de mot clé spécial pour Stub, mais vous pouvez aussi utiliser l'objet Mock pour créer des stubs.
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";
}
}
}
Point de vue des tests stub et mock:
Stub est une implémentation factice effectuée par l’utilisateur de static way signifie i.e dans Stub écrivant le code d’implémentation. Donc, il ne peut pas gérer la définition de service et la condition dynamique. Normalement, cela est fait dans le framework JUnit sans utiliser le framework moqueur.
Mock est également une implémentation factice mais sa mise en œuvre est effectuée dynamique moyen en utilisant des frameworks Mocking tels que Mockito. Nous pouvons donc traiter la définition de condition et de service de manière dynamique, c.-à-d. Que des simulations peuvent être créées dynamiquement à partir de code au moment de l’exécution. Donc, en utilisant la simulation, nous pouvons implémenter les stubs de manière dynamique.
Stub nous aide à exécuter le test. Comment? Il donne des valeurs qui aident à exécuter test. Ces valeurs ne sont pas réelles et nous avons créé ces valeurs uniquement pour exécuter le test. Par exemple, nous créons un HashMap pour nous donner des valeurs similaires à celles de la table de base de données. Ainsi, au lieu d'interagir directement avec la base de données, nous interagissons avec Hashmap.
Mock est un faux objet qui exécute le test. où nous mettons affirmer.
Exemple Mockito
Stub ne renvoie que des données prédéfinies. Les stubs sont simples et directs: ils constituent essentiellement la mise en œuvre la plus simple possible d’une méthode et renvoient à chaque fois les mêmes données prédéfinies. Cela nous donne un contrôle total sur les valeurs renvoyées par les méthodes qui ont été appelées sur la dépendance.
Mock object fournit un moyen de vérifier si l'objet testé a appelé certaines méthodes.
Et comme le disait Martin Fowler dans son essai
Il y a une différence en ce que
stub
utilise la vérification d'état tandis quemock
utilise la vérification de comportement.
Un stub est une fonction vide utilisée pour éviter les exceptions non gérées lors des tests:
function foo(){}
Une maquette est une fonction artificielle utilisée pour éviter les dépendances du système d'exploitation, de l'environnement ou du matériel pendant les tests:
function foo(bar){ window = this; return window.toString(bar); }
En termes d'affirmations et d'état:
Références
beaucoup de réponses valables là-haut, mais je pense que cela vaut la peine de mentionner ce formulaire oncle bob: https://8thlight.com/blog/uncle-bob/2014/05/14/TheLittleMocker.html
la meilleure explication jamais avec des exemples!
J'ai utilisé des exemples en python dans ma réponse pour illustrer les différences.
Stub - Le stubbing est une technique de développement logiciel utilisée pour implémenter des méthodes de classes au début du cycle de développement. Ils sont couramment utilisés comme espaces réservés pour la mise en œuvre d'une interface connue, l'interface étant finalisée ou connue, mais la mise en oeuvre n'est pas encore connue ni finalisée. Vous commencez par des stubs, ce qui signifie simplement que vous écrivez seulement la définition d'une fonction et laissez le code réel pour plus tard. L'avantage est que vous n'oublierez pas les méthodes et que vous pourrez continuer à penser à votre conception tout en la visualisant en code. Vous pouvez également demander à votre talon de renvoyer une réponse statique afin que la réponse puisse être utilisée par d'autres parties de votre code immédiatement. Les objets stub fournissent une réponse valide, mais quelle que soit l'entrée que vous transmettez, elle est statique, vous obtiendrez toujours la même réponse:
class Foo(object):
def bar1(self):
pass
def bar2(self):
#or ...
raise NotImplementedError
def bar3(self):
#or return dummy data
return "Dummy Data"
Les objets Mock sont utilisés dans des scénarios de test factice car ils valident que certaines méthodes sont appelées sur ces objets. Les objets fictifs sont des objets simulés qui imitent le comportement d'objets réels de manière contrôlée. Vous créez généralement un objet fictif pour tester le comportement d'un autre objet. Les simulacres nous permettent de simuler des ressources indisponibles ou trop lourdes pour les tests unitaires.
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 __== '__main__':
unittest.main()
C'est un exemple très basique qui exécute simplement rm et affirme le paramètre avec lequel il a été appelé. Vous pouvez utiliser une maquette avec des objets, et pas seulement des fonctions, comme indiqué ci-dessous. Vous pouvez également renvoyer une valeur afin qu'un objet fictif puisse être utilisé pour remplacer un stub à des fins de test.
Plus sur unittest.mock , remarque en python 2.x mock n’est pas inclus dans unittest mais est un module téléchargeable pouvant être téléchargé via pip (pip install mock).
J'ai également lu "The Art of Unit Testing" de Roy Osherove et je pense qu'il serait formidable qu'un livre similaire soit écrit à l'aide d'exemples Python et Python. Si quelqu'un connaît un tel livre, veuillez le partager. À votre santé :)
Un stub est un faux objet construit à des fins de test. Un mock est un stub qui enregistre si les appels attendus ont effectivement eu lieu.
Un sujet de test exécute des actions en réponse à certaines invites (appels de fonction) ou à d'autres stimuli. Voici des exemples concrets de situations de test.
Un étudiant a étudié pour devenir un technicien médical d'urgence. Allez voir Ian Gallagher dans Shameless Saison 6, Episode 10 si vous n'êtes pas familier avec cette situation de test.
Il est trop coûteux de rechercher des patients atteints de diverses maladies à des fins de test. Au lieu de cela, nous utilisons des acteurs. Nous demandons au sujet de test (Ian) "vous arrivez sur les lieux et le patient est immobilisé et inconscient, que faites-vous en premier?" Ian répond "Je vérifie si la scène est sûre". Et l'instructeur de test dit "la scène est sûre".
L'instructeur (et l'acteur) sont capables d'injecter des réponses arbitraires aux requêtes du sujet de test.
Ici, l'instructeur (et l'acteur) sont une maquette. La formation médicale utilise cette terminologie (simulation de code factice, par exemple) de la même manière que les informaticiens.
Vous testez Yahoo, un nouveau service de messagerie dont vous avez entendu parler. Pour vous inscrire, vous devez fournir votre anniversaire et des réponses à d'autres questions intrusives.
Le site Web exige que vous ayez 21 ans ou plus. Ainsi, vous entrez la valeur au 1 er janvier 1970. Elle répond aux exigences et vous évite le processus laborieux de mise en œuvre d'un flux de travail Remember-My-Birth-and-Typ-It-In.
Cette date est un bout. Cet usage de Word est spécifique à l’informatique.
A Stub est un objet qui implémente l'interface d'un composant, mais au lieu de renvoyer ce que le composant renverrait lorsqu'il serait appelé, le stub peut être configuré pour renvoyer une valeur adaptée au test. En utilisant des tronçons, un test d'unité peut tester si une unité peut gérer différentes valeurs de retour provenant de son collaborateur. Utiliser un talon au lieu d’un collaborateur réel dans un test unitaire pourrait s’exprimer ainsi:
test unitaire -> talon
test unitaire -> unit -> stub
le test unitaire confirme les résultats et l'état de l'unité
Tout d'abord, le test unitaire crée le stub et configure ses valeurs de retour. Ensuite, le test unitaire crée l’unité et y place le talon. Maintenant, le test de l'unité appelle l'unité qui, à son tour, appelle le stub. Enfin, le test unitaire fait des assertions sur les résultats des appels de méthode sur l’unité.
A Mock(c'est comme un stub, mais il a aussi des méthodes qui permettent de déterminer quelles méthodes ont été appelées sur le Mock). Il est donc possible à la fois de vérifier si l'unité peut gérer correctement différentes valeurs de retour et si l'unité utilise le collaborateur correctement. Par exemple, vous ne pouvez pas voir par la valeur renvoyée par un objet dao si les données ont été lues à partir de la base de données à l'aide d'une instruction ou d'un état préparé. Vous ne pouvez pas non plus savoir si la méthode connection.close () a été appelée avant de renvoyer la valeur. Ceci est possible avec des mocks. En d'autres termes, les simulacres permettent de tester une interaction complète d'unités avec un collaborateur. Pas seulement les méthodes collaboratrices qui renvoient les valeurs utilisées par l'unité. Utiliser une maquette dans un test unitaire pourrait être exprimé ainsi:
test unitaire -> maquette
test unitaire -> unité -> maquette
le test unitaire confirme le résultat et l'état de l'unité
test unitaire affirme sur les méthodes appelées sur maquette
Plus de détails >> Ici
Les stubs sont utilisés sur les méthodes avec une valeur de retour attendue que vous avez configurées dans votre test . Les alarmes sont utilisées sur les méthodes void qui sont vérifiées dans Assert qu'ils sont appelés.
Mock - Un mock intercepte un appel d'une méthode ou d'une fonction (ou d'un groupe de méthodes et de fonctions, comme dans le cas d'une classe simulée). Ce n'est pas une alternative à cette méthode ou fonction. Lors de cette interception, le simulacre peut faire ce qu'il veut, comme enregistrer l'entrée et la sortie, décider de court-circuiter l'appel, modifier la valeur renvoyée, etc.
Stub - Un stub est une implémentation complète valide d'une méthode ou d'une fonction (ou d'un groupe de méthodes et de fonctions comme dans le cas d'une classe stub) qui possède une interface/signature identique à la méthode, à la fonction ou au groupe de méthodes et fonctions pour lesquelles il se blesse. L'implémentation stubbed ne fait généralement que des choses acceptables dans le contexte d'un test unitaire, ce qui signifie qu'elle ne fera pas IO par exemple, en imitant le comportement de la chose qu'il stubbing.
Je lisais "L'art du test unitaire" et suis tombé sur la définition suivante:
Un fake est un terme générique qui peut être utilisé pour décrire un talon ou un objet fictif (écrit à la main ou autrement), parce qu'ils ressemblent tous les deux au vrai objet. Que ce soit un faux ou un faux dépend de la façon dont il est utilisé dans le test actuel. s'il est utilisé pour vérifier une interaction (affirmée contre), il s'agit d'un objet fictif . Sinon, il s'agit d'un souche .
Supposons que vous souhaitiez tester une classe appelée EmployeeService et qu’elle dépende d’une interface nommée EmployeeDao:
public class EmployeeService{
private EmployeeDao dao;
public EmployeeService(Dao dao){this.dao = dao;}
public String getEmployeeName(int id){
Employee emp = bar.goToDatabaseAndBringTheEmployeeWithId(id);
return emp != null?emp.getFullName:null;
}
//Further state and behavior
}
public interface EmployeeDao{
Employee goToDatabaseAndBringTheEmployeeWithId(int id);
}
Dans votre classe de test:
public class EmployeeServiceTest{
EmployeeService service;
EmployeeDao mockDao = Mockito.mock(EmployeeDao.class);//Line 3
@Before
public void setUp(){
service = new EmployeeService(mockDao);
}
//Tests
//....
}
Dans la classe de test ci-dessus en ligne 3, nous disons au framework moqueur (dans ce cas, Mockito) "Hé, Mockito, fabriquez-moi un objet qui a la fonctionnalité EmployeeDao." Le framework va créer un objet qui a la méthode goToDatabaseAndBringTheEmployeeWithId
mais en réalité sans corps. C’est à vous de dire à cette maquette ce qu’il faut faire. Ceci est une maquette.
Mais vous pouvez également créer une classe qui implémente l'interface EmployeeDao et l'utiliser à la place dans la classe de test:
public EmployeeDaoStub implements EmployeeDao{
public Employee goToDatabaseAndBringTheEmployeeWithId(int id){
//No trip to DB, just returning a dummy Employee object
return new Employee("John","Woo","123 Lincoln str");
}
}
Dans votre classe de test cette fois-ci en utilisant un bout au lieu d’une maquette:
public class EmployeeServiceTest{
EmployeeService service;
EmployeeDao daoStub = new EmployeeDaoStub();//Line 3
@Before
public void setUp(){
service = new EmployeeService(daoStub);
}
//Tests
//....
}
En résumé, les stubs sont les classes que vous créez (ou quelqu'un d'autre) pour imiter certaines dépendances simplement pour avoir l'état souhaité. Oui, comme le disent tous les autres citoyens, il s’agit principalement d’un État. Alors que les simulacres sont généralement créés par un cadre moqueur, vous n’avez aucune idée de son apparence. Mais avec les talons, vous savez quelle classe vous allez obtenir: c'est celle que vous avez créée.
Oh, au fait, si votre dépendance est une classe plutôt qu'une interface, vous pouvez simplement étendre cette classe pour créer votre stub.
Stubs et Mock remplacent les dépendances externes, mais la différence est
Stubs -> Pour tester les données
Mocks -> Pour tester le comportement
Fake/Dummy -> Ne rien tester (remplacez simplement la fonctionnalité par des méthodes vides, par exemple remplacez Logger
pour éviter tout bruit de journalisation lors des tests)
ce qui suit est ma compréhension ...
si vous créez des objets de test localement et que vous alimentez votre service local avec cela, vous utilisez un objet fantaisie. cela donnera un test pour la méthode que vous avez implémentée dans votre service local . il est utilisé pour vérifier les comportements
lorsque vous obtenez les données de test du fournisseur de services réel, mais d'une version de test de l'interface et obtenez une version de test de l'objet, vous travaillez avec des stubs vous aider à effectuer la vérification d'état ...
Une maquette est à la fois un objet technique et fonctionnel.
La maquette est technique. Il est en effet créé par une bibliothèque moqueuse (EasyMock, JMockit et plus récemment Mockito sont connus pour cela) grâce à génération de code byte.
L’implémentation factice est générée d’une manière où nous pourrions instrument lui renvoyer une valeur spécifique lorsqu’une méthode est invoquée, mais aussi d’autres choses telles que la vérification qu'une méthode fictive a été invoquée avec certains paramètres spécifiques (contrôle strict) ou quels que soient les paramètres (pas de contrôle strict).
Instancier une maquette:
@Mock Foo fooMock
Enregistrer un comportement:
when(fooMock.hello()).thenReturn("hello you!");
Vérification d'une invocation:
verify(fooMock).hello()
Celles-ci ne sont clairement pas le moyen naturel d'instancier/de remplacer le comportement/la classe Foo. C'est pourquoi je me réfère à un aspect technique.
Mais la maquette est également fonctionnelle car c'est une instance de la classe que nous devons isoler du SUT. Et avec les comportements enregistrés dessus, nous pourrions l’utiliser dans le SUT de la même manière que nous le ferions avec un talon.
Le stub est juste un fonctionnel objet: c'est une instance de la classe que nous devons isoler du SUT et c'est tout. Cela signifie que la classe de stub et tous les fixtures de comportements nécessaires lors de nos tests unitaires doivent être définis explicitement.
Par exemple, pour remplacer hello()
, il faudrait sous-classer la classe Foo
(ou implémenter son interface, elle l’a) et remplacer hello()
:
public class HelloStub extends Hello{
public String hello {
return "hello you!";
}
}
Si un autre scénario de test nécessite un autre retour de valeur, nous aurions probablement besoin de définir une méthode générique pour définir le retour:
public class HelloStub extends Hello{
public HelloStub(String helloReturn){
this.helloReturn = helloReturn;
}
public String hello {
return helloReturn;
}
}
Autre scénario: si j'avais une méthode à effets secondaires (pas de retour) et si je vérifiais que cette méthode était invoquée, j'aurais probablement ajouté un booléen ou un compteur dans la classe stub pour compter le nombre de fois que la méthode a été invoquée.
Conclusion
Le stub nécessite souvent beaucoup de temps/code à écrire pour votre test unitaire. Ce que fausse empêche grâce à fournir des fonctionnalités d'enregistrement/vérification de la boîte.
C’est pourquoi, de nos jours, l’approche stub est rarement utilisée avec l’avènement d’excellentes librairies factices.
À propos de l'article de Martin Fowler: Je ne pense pas être un programmeur "moqueur" alors que j'utilise des moqueurs et que j'évite les stubs.
Mais j’utilise mock quand c’est vraiment nécessaire (dépendances gênantes) et je préfère les tests de slicing et de mini-intégration lorsque je teste une classe avec des dépendances qui se moquent serait une surcharge.