繰り返し実行する予定のJupyterノートブックがあります。機能があり、コードの構造は次のとおりです。
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
construct_url
とscrape_url
のテストを書きたいです。これを行う最も賢明な方法は何ですか?
私が検討したいくつかのアプローチ:
doctest や nittest などのPython標準テストツールをノートブックで直接使用することができます。
Docstringに関数とテストケースがあるノートブックセル:
def add(a, b):
'''
This is a test:
>>> add(2, 2)
5
'''
return a + b
Docstring内のすべてのテストケースを実行するノートブックセル(ノートブックの最後のセル):
import doctest
doctest.testmod(verbose=True)
出力:
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.
機能を持つノートブックセル:
def add(a, b):
return a + b
テストケースを含むノートブックセル(ノートブックの最後のセル)。セルが実行されると、セルの最後の行でテストケースが実行されます。
import unittest
class TestNotebook(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 2), 5)
unittest.main(argv=[''], verbosity=2, exit=False)
出力:
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)
失敗したテストのデバッグ中に、ある時点でテストケースの実行を停止してデバッガーを実行すると便利な場合があります。このため、実行を停止する行の直前に次のコードを挿入します。
import pdb; pdb.set_trace()
例えば:
def add(a, b):
'''
This is the test:
>>> add(2, 2)
5
'''
import pdb; pdb.set_trace()
return a + b
この例では、次にdoctestを実行すると、returnステートメントの直前で実行が停止し、 Pythonデバッガー (pdb)が開始されます。ノートブックでpdbプロンプトを直接取得します。これにより、a
およびb
の値を調べたり、行をステップオーバーしたりできます。
実験用のJupyterノートブック を、今説明した手法で作成しました。
私の意見では、Jupyterノートブックでユニットテストを行う最良の方法は次のパッケージです。 https://github.com/JoaoFelipe/ipython-unittest
パッケージドキュメントの例:
%%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
コンテキストを考えると、 doctests for construct_url
&scrape_url
このようなノートブックのセルの内側、
def construct_url(data):
'''
>>> data = fetch_test_data_from_somewhere()
>>> construct_url(data)
'http://some-constructed-url/'
'''
...
<actual function>
...
次に、下部の別のセルでそれらを実行できます。
import doctest
doctest.testmod(verbose=True)
また、ノートブックでdoctestsおよびunittestsを実行するために使用できるJupyter Notebooksのテストライブラリ treon を構築しました。また、新しいカーネルでノートブックを上から下に実行し、実行エラーを報告することもできます(健全性テスト)。
少し研究した後、私は自分のテストコードが次のようになっている独自のソリューションに到達しました
_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.')
_
それは何ですか
a
がb
と等しいかどうかを確認します。all()
が真であるかどうかをチェックします。私はノートブックの上に関数を置き、私はこのような何かをテストします
_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.
_
このアプローチの長所は
doctest.testmod(verbose=True)
のような特別なコードを追加する必要はありません。