web-dev-qa-db-ja.com

Jupyterノートブックの機能の単体テスト?

繰り返し実行する予定の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_urlscrape_urlのテストを書きたいです。これを行う最も賢明な方法は何ですか?

私が検討したいくつかのアプローチ:

  • 関数をユーティリティファイルに移動し、そのユーティリティファイルのテストを標準のPythonテストライブラリに書き込みます。おそらく最良のオプションですが、すべてのコードがノート。
  • テストデータを使用して、ノートブック自体にアサートを書き込みます(ノートブックにノイズを追加します)。
  • 特殊なJupyterテストを使用してセルのコンテンツをテストします(セルのコンテンツが変更されるため、これが機能するとは思わないでください)。
27
Richard

doctestnittest などのPython標準テストツールをノートブックで直接使用することができます。

Doctest

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ノートブック を、今説明した手法で作成しました。

28

私の意見では、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
2
Michael D

コンテキストを考えると、 doctests for construct_urlscrape_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 を構築しました。また、新しいカーネルでノートブックを上から下に実行し、実行エラーを報告することもできます(健全性テスト)。

0
amirathi

少し研究した後、私は自分のテストコードが次のようになっている独自のソリューションに到達しました

_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.')
_

それは何ですか

  • abと等しいかどうかを確認します。
  • それらが異なる場合、引数は赤で表示されます。
  • それらが同じ場合、「OK」と表示されます。
  • 比較の結果が配列の場合、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を使用する場合に追加する必要があるdoctest.testmod(verbose=True)のような特別なコードを追加する必要はありません。
  • エラーメッセージは簡単です。
  • テスト(アサート)コードをカスタマイズできます。
0
Sanghyun Lee