[GO] Module de débogage et de test Python

Débogage et tests unitaires

assert déclaration

Déclenche une exception AssertionError si la variable test est false.

test = 0
data = "assertion error"

try:
    assert test,data
except AssertionError:
    print data
finally:
    print "the end"

S'il ne s'agit que de la partie instruction assert, vous pouvez la remplacer en écrivant comme suit.

if __debug__:
  if not test
    raise AssertionError, data

Traitement de la séparation des exceptions et affichage du contenu

somedata = 1

#Abattez les exceptions que vous voulez attraper.
fatal_exceptions = (KeyboardInterrupt, MemoryError)

try:
    assert somedata
except fatal_exceptions, inst:  #Vous pouvez recevoir le contenu de l'exception avec l'argument inst.
    print type(inst)    #Afficher le type d'exception
    print inst          #Afficher le contenu de l'exception
    raise
#Attrapez toutes les autres exceptions
except Exception, inst:
    print type(inst)    #Afficher le type d'exception
    print inst          #Afficher le contenu de l'exception
finally:
    print "the end"

UnitTest Exemples légèrement modifiés et ajoutés du tutoriel officiel.

import random
import unittest

class TestSequenceFunctions(unittest.TestCase):

    #Processus d'initialisation appelé à chaque fois
    #En plus de cela, il existe une méthode appelée après l'exécution du test, etc.
    def setUp(self):
        self.seq = range(10)

    #Décrivez le nom de la méthode en commençant par test.
    def test_shuffle(self):
        random.shuffle(self.seq)
        self.seq.sort()

        #Vérifiez que les deux arguments sont égaux.
        #Vérifier l'inégalité est assertNotEqual()Vous pouvez le faire avec.
        self.assertEqual(self.seq, range(10))

        #Vérifiez les exceptions.
        # assertRaises(exception, callable, *args, **kwds)
        #Passez args et kwds à la fonction du deuxième argument et vérifiez que l'exception spécifiée par le premier argument se produit.
        self.assertRaises(TypeError, random.shuffle, (1,2,3))

    def test_choice(self):
        element = random.choice(self.seq)

        #Vérifiez que la valeur de l'argument est True.
        # bool(element)Équivaut à est vrai
        self.assertTrue(element in self.seq)

    def test_sample(self):

        #Si seul l'argument d'exception est passé, le gestionnaire de contexte est renvoyé.
        #Vous pouvez écrire le code à tester en ligne.
        with self.assertRaises(ValueError):
            random.sample(self.seq, 20)
        for element in random.sample(self.seq, 5):
            self.assertTrue(element in self.seq)

if __name__ == '__main__':
    
    # main()Peut être exécuté avec.
    unittest.main()

    #Faites exécuter des tests individuellement.
    _test_choice = TestSequenceFunctions('test_choice')
    _test_sample = TestSequenceFunctions('test_sample')

    #Il peut être enregistré dans la suite de tests et exécuté collectivement par le coureur.
    TestSuite = unittest.TestSuite()
    TestSuite.addTest(_test_choice)
    TestSuite.addTest(_test_sample)
    runner = unittest.TextTestRunner()
    runner.run(TestSuite)

    #Vous pouvez obtenir des fonctions de test collectivement avec le chargeur.
    suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
    unittest.TextTestRunner(verbosity=2).run(suite)

traceback La pile est la zone de mémoire où le programme est en cours d'exécution. Une trace de pile enregistre l'état d'exécution (nom de la fonction, nom de la fonction appelante, ligne, instruction, nom de fichier) du programme dans la zone de mémoire, et il s'agit d'un objet dans les langages de script tels que Python. Il est fourni.

Tout d'abord, les informations de trace de pile peuvent être obtenues à partir de sys.exc_info ().

exc_type, exc_value, exc_traceback = sys.exc_info()

En utilisant ces trois variables comme arguments, traceback.print_tb () et traceback.print_exception () affichent des informations.

Cependant, si vous utilisez traceback.print_exc () ci-dessous, vous pouvez omettre l'acquisition de variable de sys.exc_info (), et vous pouvez également afficher le type et le contenu, ce qui est pratique. En gros, vous pouvez l'utiliser.

import sys
import traceback

def somework():
    try:
        print a #Utiliser des variables non définies
    except Exception:
        print "error"
        traceback.print_exc(file=sys.stdout)
    finally:
        print "the end"

if __name__ == '__main__':
    somework()

Résultat de sortie Le nom du module, la ligne, le nom de la fonction, l'instruction de cause et le message d'erreur de cause sont affichés.

error
Traceback (most recent call last):
  File "/Users/test.py", line 8, in somework
    print a
NameError: global name 'a' is not defined
the end

Il s'agit de la sortie lorsque la traceback n'est pas écrite. Rien n'est sorti, juste en passant l'exception.

error
the end

Ensuite, une méthode d'acquisition des informations de trace de pile sous forme de liste comprenant des taples est présentée. Voici également un exemple d'obtention d'une variable depuis sys.exc_info (). (Parce qu'il est utilisé comme argument)

import sys
import traceback

def somework():
    try:
        print a #Utiliser des variables non définies
    except Exception:
        print "error"
        exc_type, exc_value, exc_traceback = sys.exc_info()
        print traceback.extract_tb(exc_traceback) #Obtenez uniquement des informations actuelles
        print traceback.extract_stack() #Obtenez une touche contenant les informations de fonction de l'appelant

        #Appelez-le si vous voulez simplement le recevoir dans une liste au lieu d'un tap et le rendre plus facile à afficher.
        traceback.format_tb(exc_traceback)
        traceback.format_stack()

        print "the end"

if __name__ == '__main__':
    somework()

nosetest

J'ai fait référence à la «programmation professionnelle Python».

installation du nez

pip install nose

Classe à tester Cette classe ajoute ou soustrait simplement des nombres à votre compte bancaire.

# coding: UTF-8

class NotEnoughFundsException(Exception):
    pass

class BankAccount(object):

    def __init__(self):
        self._balance = 0

    def deposit(self, amount):
        self.balance += amount

    def withdraw(self, amount):
        self.balance -= amount

    def get_balance(self):
        return self._balance

    def set_balance(self, value):
        if value < 0:
            raise NotEnoughFundsException

        self._balance = value

    #Préparez un emballage pour pouvoir utiliser des getters et des setters.
    #équilibrer=Ou+Ces fonctions seront appelées automatiquement en accédant avec.
    balance = property(get_balance, set_balance)

Écrivez du code pour tester avec le nez. Ici, la préparation d'une valeur fixe pour un test telle que 100 ou 0 est appelée un appareil de données.

# coding: UTF-8

import unittest

class BankAccountTest(unittest.TestCase):

    def _getTarget(self):
        from bankaccount import BankAccount
        return BankAccount

    #Créez une instance à tester.
    def _makeOne(self, *args, **kwargs):
        return self._getTarget()(*args, **kwargs)

    def test_construct(self):
        target = self._makeOne()
        self.assertEqual(target._balance, 0)

    def test_deposit(self):
        target = self._makeOne()
        target.deposit(100)
        self.assertEqual(target._balance, 100)

    def test_withdraw(self):
        target = self._makeOne()
        target._balance = 100
        target.withdraw(20)
        self.assertEqual(target._balance, 80)

    def test_get_blance(self):
        target = self._makeOne()
        target._balance = 500
        self.assertEqual(target.get_balance(), 500)

    def test_set_balance_not_enough_funds(self):
        target = self._makeOne()
        from bankaccount import NotEnoughFundsException
        try:
            target.set_balance(-1)
            self.fail()
        except NotEnoughFundsException:
            pass

Placez les deux ci-dessus dans le même répertoire et exécutez nose dans ce répertoire.

nosetest

.....
----------------------------------------------------------------------
Ran 5 tests in 0.004s

OK

Obtenez une couverture

pip install coverage
# nosetests -v --with-coverage
test_construct (test_bankaccount.BankAccountTest) ... ok
test_deposit (test_bankaccount.BankAccountTest) ... ok
test_get_blance (test_bankaccount.BankAccountTest) ... ok
test_set_balance_not_enough_funds (test_bankaccount.BankAccountTest) ... ok
test_withdraw (test_bankaccount.BankAccountTest) ... ok

Name          Stmts   Miss  Cover   Missing
-------------------------------------------
bankaccount      16      0   100%
----------------------------------------------------------------------
Ran 5 tests in 0.006s

OK

Une couverture facile à lire est sortie.

Sortie au format XUNIT avec l'option "--with-xunit".

# nosetests -v -w . --with-coverage --with-xunit
test_construct (test_bankaccount.BankAccountTest) ... ok
test_deposit (test_bankaccount.BankAccountTest) ... ok
test_get_blance (test_bankaccount.BankAccountTest) ... ok
test_set_balance_not_enough_funds (test_bankaccount.BankAccountTest) ... ok
test_withdraw (test_bankaccount.BankAccountTest) ... ok

----------------------------------------------------------------------
XML: nosetests.xml
Name          Stmts   Miss  Cover   Missing
-------------------------------------------
bankaccount      16      0   100%
----------------------------------------------------------------------
Ran 5 tests in 0.006s

OK

Sortie en XML pour qu'il puisse être lu par jenkins.

coverage xml

mock

Utilisation normale

Classe à tester testモジュールのTest.py

# coding: UTF-8

class Widget(object):

    def __init__(self):
        self.value = 10

    def Additional(self, add):
        self.value += add
        return self.value

Code de test

from mock import Mock
from test import Test

def mock_value(value):
    return 20 + value

if __name__ == '__main__':

    #Remplacez la fonction par une simulation qui renvoie 100 de manière fixe.
    Test.Widget.Additional = Mock(return_value=100)

    w = Test.Widget()
    print w.Additional(1)

    #Si vous voulez faire un calcul au lieu d'une valeur fixe, côté_Passez une fonction factice en vigueur.
    Test.Widget.Additional = Mock(side_effect=mock_value)
    print w.Additional(1)
100
21

Remplacer la classe par une maquette

from mock import Mock
from test import Test

def mock_value(value):
    return 20 + value

if __name__ == '__main__':

    #Remplacez la classe elle-même.
    Test.Widget = Mock()
    #La valeur renvoyée par la fonction est fixe.
    Test.Widget.return_value.Additional.return_value = 10

    w = Test.Widget()
    print w.Additional(1)

    #Fonctions d'échange
    Test.Widget.return_value.Additional = mock_value
    print w.Additional(1)

patch Remplacez l'objet fictif dans lequel la fonction s'exécute par l'objet réel.

# coding: UTF-8

from mock import patch

#Spécifiez la méthode et la valeur de retour à remplacer uniquement pendant l'exécution de cette fonction
@patch("Test.Widget.Additional", return_value=10)
def test_func(m): 

    import Test
    w = Test.Widget()
    print w.Additional(1)
    assert w.Additional(1) == 10

    #Au fait, m reçu comme argument est une fonction supplémentaire remplacée par un simulacre.
    #Ici aussi, le résultat est 10.
    #Cette fois j'ai remplacé la méthode, mais dans le cas d'une classe, elle devient une classe.
    print m()

if __name__ == '__main__':
    test_func()

Pour le format du gestionnaire de contexte en utilisant avec

# coding: UTF-8

from mock import patch

def test_func():

    #Spécifiez la méthode et la valeur de retour à remplacer uniquement pendant l'exécution de la portée
    with patch("Test.Widget.Additional", return_value=10) as m:
        import Test
        w = Test.Widget()
        print w.Additional(1)
        assert w.Additional(1) == 10
        print m()

if __name__ == '__main__':
    test_func()

Spécification du type de classe

mock = Mock(spec=SomeClass)
isinstance(mock, SomeClass) #Cela réussit

Donner des attributs spécifiques à Mock

# coding: UTF-8
from mock import Mock, patch

if __name__ == '__main__':
    #Une méthode qui renvoie 3 avec la méthode name,
    #Une méthode qui déclenche une exception KeyError avec le nom autre
    attrs = {'method.return_value': 3, 'other.side_effect': KeyError}

    #Vous pouvez ajouter des attributs en même temps que déclarer(some_attribute)。
    mock = Mock(some_attribute='eggs', **attrs)

    print mock.some_attribute
    print mock.method()
    print mock.other()

Autre

Une autre chose qui semble utile

@patch('sys.stdout', new_callable=StringIO)

Ainsi, il sera créé en tant qu'objet StringIO au moment de la création.

Vous pouvez écrire une pseudo-classe pour le test et l'assigner avec "Test.Widget =" sans utiliser mock. Si vous n'utilisez pas la fonction de validation de simulation, cette méthode peut être plus rapide.

module django-webtest

Très utile pour tester Django.

pip install webtest
pip install django-webtest

L'application Django ressemble à ceci:

mysite
|-mysite
|   |-__init__.py
|   |-settings.py
|   |-urls.py
|   |-wsgi.py
|-test app
|   |-__init__.py
|   |-form.py
|   |-modes.py
|   |-views.py
|   |-tests.py Ecrivez ce test dans ce fichier|
|-templates
|-manage.py

Écrivez le contenu de tests.py comme suit

from django_webtest import WebTest

class TestIndex(WebTest):

    def test_index(self):
        res = self.app.get("/") #Obtenez la réponse

        #Vérifiez le code d'état et le contenu.
        assert res.status == '200 OK'
        assert 'html' in res

Démarrez le serveur de développement avec manage.py runserver, puis exécutez le test avec la commande suivante.

# sudo python manage.py test testapp

Résultat de sortie

Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.175s

OK
Destroying test database for alias 'default'...

Recommended Posts

Module de débogage et de test Python
Coopération entre le module python et l'API
Module de socket Python 3 et flux de communication de socket
Importer le module Python
Module d'implémentation de file d'attente et Python "deque"
Distribution et test
Test d'intégrité Python
Module de collections Python
Importation de modules et gestion des exceptions en python
[Python] Type de classe et utilisation du module datetime
[python] Compresser et décompresser
Jugement des nombres premiers par Python
Jugement des nombres premiers avec Python
Astuces Python et Numpy
[Python] pip et roue
Module Python (mémo d'apprentissage Python ④)
Créer un module Python
importation du module d'origine python
Paquets et modules Python
Mémo du package de test Python
Intégration Vue-Cli et Python
Ruby, Python et carte
Jugement des nombres premiers avec python
entrée et sortie python
Python et Ruby se séparent
test de coopération de balises python
modèle de test unitaire python
Python asyncio et ContextVar
Exemple d'obtention du nom du module et du nom de la classe en Python
Module [Python of Hikari-] Chapitre 08-03 (Importation et utilisation de la bibliothèque standard)
Programmation avec Python et Tkinter
Veriloggen et cocotb sont utilisés pour concevoir et tester Verilog en Python uniquement.
Chiffrement et déchiffrement avec Python
Trier les importations de modules Python par ordre alphabétique
Python: variables de classe et d'instance
Fonctionnement de l'importation de modules Python
3-3, chaîne Python et code de caractère
Série Python 2 et série 3 (édition Anaconda)
Python sur Ruby et Ruby en colère sur Python
Indentation Python et format de chaîne
division des nombres réels python (/) et division des nombres entiers (//)
Installez Python et Flask (Windows 10)
[Python] ModuleNotFoundError: aucun module nommé "urlparse"
À propos des objets et des classes Python
À propos des variables et des objets Python
Algorithme en Python (jugement premier)
Module Python num2words Différence de comportement entre l'anglais et le russe
Apache mod_auth_tkt et Python AuthTkt
Å (Ongustorome) et NFC @ Python
À propos du module Python venv
Apprenez à connaître les packages et les modules Python
# 2 [python3] Séparation et commentaire
Copie superficielle Python et copie profonde
Mémo tranche python et rubis
Installation de Python et grammaire de base
Copie superficielle Python et copie profonde
Déboguer Python avec VS Code
À propos de Python, len () et randint ()