私が歌川です

@utgwkk が書いている

Python の unittest で標準出力に表示される内容のテストをする

tl;dr

  • 出力する文字列を生成する関数を作り、そのテストを書くべきだと私は思う
  • sys.stdout をキャプチャすれば表示内容のテストが書ける
  • test.support.capture_stdout()コンテクストマネージャを使うべき
  • そもそも unittest でやるべきではない

はじめに

いろいろな環境があります。ほとんど標準ライブラリしか使えないとか、莫大な量の print があるとか、いろいろあります。 我々はそういった環境においてもやっていくことになります。したがって、やっていきましょう。

暗黙のうちに Python 3.5 以降を前提としています。

sys.stdout をキャプチャする

コード

import sys
from io import StringIO

io = StringIO()

# 標準出力を io に結びつける
sys.stdout = io

print('hoge')

# 標準出力を元に戻す
sys.stdout = sys.__stdout__

print('captured: {}'.format(io.getvalue())) # captured: hoge\n

解説

ご存知の通り、UNIX 世界において標準出力はファイルです。そして、Python においてもそうです。 さらに、Python にはファイルを模倣したようなオブジェクトが用意されています。それが io.StringIO です。 これを sys.stdout に放り込めば、標準出力を変数にリダイレクトするといったことができます。

import sys
from io import StringIO

io = StringIO()
sys.stdout = io

さてあとは普段通りに print するだけです。そうすると、io に、出力されるはずだった文字列が蓄積されていきます。

print('this')
print('is')
print('test')

満足しましたか? それでは io に蓄積されたものを見ていきましょう。おっと sys.stdout を元の標準出力に戻すことを忘れずに。

sys.stdout = sys.__stdout__
print('captured: {}'.format(io.getvalue()))

sys.__stdout__ には、プログラム起動時の標準出力を示すオブジェクトが格納されています。 こうして、我々は標準出力に表示される内容をキャプチャする方法を知りました。

標準出力に表示される内容のテストを書く

コード

import sys


def method_one():
    print('called method_one()')


def method_two():
    print('called method_two()')
    print('not captured', file=sys.stderr)
import sys
import unittest
import hoge
from io import StringIO


class HogeTest(unittest.TestCase):
    def setUp(self):
        self.captor = StringIO()
        sys.stdout = self.captor

    def tearDown(self):
        sys.stdout = sys.__stdout__

    def test_method_one(self):
        hoge.method_one()
        self.assertEqual(self.captor.getvalue(), 'called method_one()\n')

    def test_method_two(self):
        hoge.method_two()
        self.assertEqual(self.captor.getvalue(), 'called method_two()\n')

if __name__ == '__main__':
    unittest.main()

解説

さて、キャプチャされた文字列を取得する方法まで分かれば、あとは unittest の流儀に沿って書いていけばよいです。 改行コードのことだけ忘れないように!

まとめ

もっとテストの書きやすい設計にしてくれ