0
0

Jupyter・Colabのコードセルでテストするメモ

Posted at

概要

Jupyter/ColabのPythonコードセルで標準のdoctestやunittestが動かせたのでメモとして残しておく。

doctest

一般的にはメソッド内にdoctestを記述してdoctest.testmod()を呼び出すことでテストを実行する仕組みですが、コードセル内でも同様のコードでテストすることができます。vervose=Trueを渡すとテストが成功した場合でも何をテストをしたのわかるので渡すようにしています。(Falseだと成功した場合は何も出力しません)

def add(a, b):
    """
    >>> add(1, 2)
    3
    """

    return a + b

if __name__ == "__main__":
    import doctest
    doctest.testmod(verbose=True)

関数内にテストコードを書かずに別のセルに書く事もできます。一般的には別ファイルにテストコードを書いてdoctest.testfile()で読み込んでテストするのですが、doctest.run_docstring_examples()を使えばテストコードをファイルではなく文字列で渡してテストすることができるようです。

def add3(a, b, c):
    return a + b + c
import doctest

teststr = """
>>> add3(1, 2, 3)
6
"""

doctest.run_docstring_examples(teststr, globals(), verbose=True)

最終的にPythonスクリプトを出力する前提で、出力したいコード部分を%%writefileマジックコマンドをつけたコードセルに書いておき、出力したPythonファイルに対して別のコードセルからテストを実行できると便利なのでやってみました。ただ単純にimportするだけだと更新前のコードをキャッシュしてコードの変更が反映されないので、ちょっと工夫する必要がありました。

%%writefile add5.py

def sum5(a, b, c, d, e):
    return a + b + c + d + e
import doctest
import sys
import importlib

sys.dont_write_bytecode = True

import add5
importlib.reload(add5)
from add5 import sum5


teststr = """
>>> sum5(1, 2, 3, 4, 5)
15
"""

doctest.run_docstring_examples(teststr, globals(), verbose=True)

sys.dont_write_bytecode = Trueはimportの際__pycache__ディレクトリにバイトコードを出力させないようにする設定です。変更の反映には影響なさそうですが、コーディング・テスト中にバイトコードを出力されてもしょうがないのでつけています。
importlib.reload(add5)を呼び出すことで変更したコード(モジュール)を再度読み直してくれて変更が反映されるようです。

Colabでも全く同じようにテストの実行が可能なので、確認用に用意してみました。

unittest

unittest標準ライブラリでもdoctestと同じようにコードセルからテストができるようです。

一般的にはunittest.TestCaseを継承したテストクラスを定義してunittest.main()を呼び出すのですが、コードセルでも同様にして実行できます。ただし、コードセル上では引数としてargvexit=Falseを渡さないとエラーが出るようです。

import unittest


def add(a, b):
    return a + b


class TestNotebook(unittest.TestCase):

    def test_add(self):
        self.assertEqual(add(2, 2), 4)


unittest.main(argv=[""], verbosity=2, exit=False)

普通に考えればそりゃそうだろうとは思いますが、一応テスト対象のコードとテストクラスを別々のコードセルに書くこともできます

def add3(a, b, c):
    return a + b + c
import unittest

class TestNotebook3(unittest.TestCase):

    def test_add3(self):
        self.assertEqual(add3(2, 2, 2), 6)

unittest.main(argv=["", "TestNotebook3.test_add3"], verbosity=2, exit=False)

引数argvに渡すリストの2番目に実行したいメソッドを渡すとそのメソッドだけテストを実行してくれます。何も指定しなかった場合は実行済みのコードセルに書いたテストクラスすべてをテストします。

同様に、別ファイルに出力したPythonファイルに対して別のコードセルからテストを実行することができます。

%%writefile add6.py

def sum6(a, b, c, d, e, f):
    return a + b + c + d + e + f
import unittest

import sys
import importlib

sys.dont_write_bytecode = True

import add6
importlib.reload(add6)
from add6 import sum6

class TestNotebook6(unittest.TestCase):

    def test_sum6(self):
        self.assertEqual(sum6(1, 2, 3, 4, 5, 6), 21)

unittest.main(argv=["", "TestNotebook6.test_sum6"], verbosity=2, exit=False)

こちらもColabでもテスト実行可能なので、用意しました

感想

Jupyterはコードの試行錯誤やテストにもってこいの環境であり、そこにテストライブラリを導入することによりさらにJupyter内でPythonコードの完成度を上げることができるので積極的に活用したい。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0