J'ai un ordinateur portable Jupyter que je compte utiliser à plusieurs reprises. Il a des fonctions, la structure du code est la suivante:
def construct_url(data):
...
return url
def scrape_url(url):
... # fetch url, extract data
return parsed_data
for i in mylist:
url = construct_url(i)
data = scrape_url(url)
... # use the data to do analysis
J'aimerais écrire des tests pour construct_url
et scrape_url
. Quelle est la manière la plus sensée de faire cela?
Quelques approches que j'ai envisagées:
Il est possible d’utiliser des outils de test standard Python, tels que doctest ou unittest , directement dans le cahier.
Une cellule de bloc-notes avec une fonction et un scénario de test dans une docstring:
def add(a, b):
'''
This is a test:
>>> add(2, 2)
5
'''
return a + b
Une cellule de bloc-notes (la dernière dans le bloc-notes) qui exécute tous les tests dans les docstrings:
import doctest
doctest.testmod(verbose=True)
Sortie:
Trying:
add(2, 2)
Expecting:
5
**********************************************************************
File "__main__", line 4, in __main__.add
Failed example:
add(2, 2)
Expected:
5
Got:
4
1 items had no tests:
__main__
**********************************************************************
1 items had failures:
1 of 1 in __main__.add
1 tests in 2 items.
0 passed and 1 failed.
***Test Failed*** 1 failures.
Une cellule de cahier avec une fonction:
def add(a, b):
return a + b
Une cellule de bloc-notes (la dernière dans le bloc-notes) contenant un scénario de test. La dernière ligne de la cellule exécute le scénario de test lors de l'exécution de la cellule:
import unittest
class TestNotebook(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 2), 5)
unittest.main(argv=[''], verbosity=2, exit=False)
Sortie:
test_add (__main__.TestNotebook) ... FAIL
======================================================================
FAIL: test_add (__main__.TestNotebook)
----------------------------------------------------------------------
Traceback (most recent call last):
File "<ipython-input-15-4409ad9ffaea>", line 6, in test_add
self.assertEqual(add(2, 2), 5)
AssertionError: 4 != 5
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
Lors du débogage d'un test ayant échoué, il est souvent utile d'interrompre l'exécution du scénario de test à un moment donné et d'exécuter un débogueur. Pour cela, insérez le code suivant juste avant la ligne sur laquelle vous voulez que l'exécution s'arrête:
import pdb; pdb.set_trace()
Par exemple:
def add(a, b):
'''
This is the test:
>>> add(2, 2)
5
'''
import pdb; pdb.set_trace()
return a + b
Pour cet exemple, la prochaine fois que vous exécuterez le test, l’exécution s’arrêtera juste avant l’instruction return et le débogueur Python (pdb) démarrera. Vous obtiendrez une invite pdb directement dans le bloc-notes, ce qui vous permettra d'inspecter les valeurs de a
et b
, des lignes successives, etc.
J'ai créé un Jupyter notebook pour expérimenter avec les techniques que je viens de décrire.
À mon avis, le meilleur moyen d’avoir un test unitaire dans le cahier Jupyter est le package suivant: https://github.com/JoaoFelipe/ipython-unittest
exemple tiré du paquet docs:
%%unittest_testcase
def test_1_plus_1_equals_2(self):
sum = 1 + 1
self.assertEqual(sum, 2)
def test_2_plus_2_equals_4(self):
self.assertEqual(2 + 2, 4)
Success
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Après des recherches un peu, j'ai atteint ma propre solution où j'ai mon propre code de test qui ressemble à ceci
def red(text):
print('\x1b[31m{}\x1b[0m'.format(text))
def assertEquals(a, b):
res = a == b
if type(res) is bool:
if not res:
red('"{}" is not "{}"'.format(a, b))
return
else:
if not res.all():
red('"{}" is not "{}"'.format(a, b))
return
print('Assert okay.')
Qu'est-ce qu'il fait est
a
est égal à b
.all()
est vrai.Je mets la fonction en haut de mon cahier et je teste quelque chose comme ça
def add(a, b):
return a + b
assertEquals(add(1, 2), 3)
assertEquals(add(1, 2), 2)
assertEquals([add(1, 2), add(2, 2)], [3, 4])
---
Assert okay.
"3" is not "2" # This is shown in red.
Assert okay.
Les avantages de cette approche sont
doctest.testmod(verbose=True)
, que je dois ajouter si j'utilise doctest.