Maison  >  Article  >  développement back-end  >  Classes Python moqueuses

Classes Python moqueuses

王林
王林original
2024-08-28 18:32:30366parcourir

Mocking Python Classes

Dernièrement, j'ai dû écrire des tests unitaires en utilisant Pytest pour un module Python. Le module contient une classe où d'autres classes sont initialisées au sein de son constructeur.

Comme d'habitude, j'ai créé un dispositif pour cette classe afin de faciliter l'écriture d'un test pour chaque méthode de classe. À ce stade, j'ai rencontré quelques problèmes lorsque j'ai essayé de me moquer des différentes classes initiées dans le constructeur. Les moqueries n'ont pas fonctionné et des instances de ces classes étaient toujours en cours de création.

Après quelques recherches et en combinant quelques solutions différentes que j'ai trouvées en ligne, je souhaite partager comment j'ai réussi à me moquer des cours.

Solution

Voici un exemple de la classe dont j'ai essayé de me moquer :

class ClassA:
    def __init__(self):
        self.class_b = ClassB()
        self.class_c = ClassC()
        self.count = 0

Nous souhaitons définir une valeur pour chaque champ de cette classe lors des tests. Cette valeur peut être Aucune ou une classe simulée, mais nous ne voulons pas d'initiations des classes ClassB et ClassC.

Dans notre cas, décidons que self.class_b et self.class_c doivent être simulés :

@pytest.fixture
def mock_class_b():
    class_b = Mock(spec=ClassB)
    return class_b

@pytest.fixture
def mock_class_c():
    class_c = Mock(spec=ClassC)
    return class_c

Donc, un montage pour cette classe qui sert notre objectif ressemble à ceci :

@pytest.fixture
def class_a_mock(mock_class_b, mock_class_c):
    with patch.object(target=ClassA, attribute="__init__", return_value=None) as mock_init:
        class_a = ClassA()
        class_a.class_b = mock_class_b
        class_a.class_c = mock_class_c
        class_b.count = 0
        return class_a

La partie importante est de savoir comment utiliser la fonction patch.object, qui provient du module unittest.mock en Python. Il est utilisé dans les tests pour remplacer temporairement un attribut d'un objet donné par une valeur fictive ou une autre valeur.

Arguments

  1. target=ClassA : l'objet (généralement une classe) dont nous voulons patcher l'attribut.
  2. attribut="__init__" : le nom de l'attribut que nous voulons patcher.
  3. return_value=None : remplacement de la méthode __init__ par une fonction qui ne fait rien

De cette façon, nous pouvons créer des variables simulées dans notre luminaire.
En savoir plus sur patch.object

Conseils de test

J'ai écrit ce tutoriel pour ce genre de cas où, pour une raison quelconque, on ne peut pas changer le code de la Classe A. Cependant, je recommande généralement de modifier le code si possible, non pas pour changer la logique, mais pour le rendre plus testable .

Voici quelques exemples de modifications de la classe A pour la rendre plus testable :

Option 1 : Passer les instances de classe B et de classe C comme paramètres.
De cette façon, lorsque nous écrivons le luminaire, nous pouvons transmettre des simulations au lieu d'instances.

class ClassA:
    def __init__(self, class_b_instance, class_c_instance):
        self.class_b = class_b_instance
        self.class_c = class_c_instance
        self.count = 0

Option 2 : Créez une variable booléenne qui indique le mode test.
De cette façon, nous pouvons décider quels champs de classe A obtiendront ou non une valeur lors de son initiation.

class ClassA:
    def __init__(self, test_mode=False):
        if not test_mode:
            self.class_b = ClassB()
            self.class_c = ClassC()
        self.count = 0

Option 3 : Effectuer des initiations de cours selon une méthode distincte.
Cette approche nous donne le choix d'éviter d'appeler set_class_variables dans le module de test.

class ClassA:
    def __init__(self):
        self.class_b = None
        self.class_c = None
        self.count = None

    def set_class_variables(self):
        self.class_b = ClassB()
        self.class_c = ClassC()
        self.count = 0

J'espère que cela vous aidera ! :)

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn