Python pytestでfixtureを使う

Python学習【365日チャレンジ!】183日目のマスターU(@Udemy11)です。

マイナポイントの申込みで何も考えずPayPayの決済サービスにしたんだけど、やっぱり違うサービスに変更したいと思ってサポートに連絡したら、変更はできませんの一点張りでほんとどうなってんだと怒りMaxオリックスになってしまいました。

確かにサイトに「一度決定したら変更はできません」と書いてますけど、期間限定で手続き無効にして変更可能にしてもいいんじゃないと思うのは私だけ?

政府からの莫大な委託料を受けてるなら、この程度の国民サービスを用意するのが普通なんじゃない?といまだに怒りが収まりません。

ま~、ちゃんと確認して申し込まない自分が悪いんだけどね。

では、そんな怒りをパワーにして、今日もPython学習を頑張りましょう!

昨日の復習

昨日は、pytestのfixtureのtmpdirの簡単な使い方を学習しました。

fixtureを使うことでコマンドから値を受け取ったり、一時的なディレクトリを作って削除したりすることができました。

昨日は、一時ディレクトリを作成できるtmpdirを簡単に使う方法を学習しました。

今日は、pytestでのtmpdirの具体的な使い方を学習します。

calculation.py

今回もメインのファイルはcalculation.pyですが、前回と同じコードに加えて、少し関数を加えています。

import os

class Cal(object):
    def add_and_double(self, x, y):
        if type(x) is not int or type(y) is not int:
            raise ValueError
        result = x + y
        result *= 2
        return result
    
    def save(self, dir_path, file_name):
        if not os.path.exists(dir_path):
            os.mkdir(dir_path)
        file_path = os.path.join(dir_path, file_name)
        with open(file_path, 'w') as f:
            f.write('test')

これまでのコードに加えたのは、save()関数で、dir_pathfile_nameを引数にとって、パスが存在しなければディレクトリを作成し、そのディレクトリの中に文字列のtestを書き込むファイルを作成するコードです

test_calculation.py

テストするコードは前回から少し変更します。

import os

import calculation

class TestCal(object):

    @classmethod
    def setup_class(cls):
        cls.cal = calculation.Cal()
        cls.test_file_name = 'test.txt'

    def test_add_and_double(self):
        assert self.cal.add_and_double(1, 3) == 8

    def test_save(self, tmpdir):
        self.cal.save(tmpdir, self.test_file_name)
        test_file_path = os.path.join(tmpdir, self.test_file_name)
        assert os.path.exists(test_file_path) is True

17行目でos.pathを使うので、最初にosライブラリをインポートしています。

次に、これまでに学習した@classmethodを使って、クラスオブジェクトとグローバル関数を定義します。

その後、これまでの関数のテストに加え、15行目から18行目で新しくメインファイルに追加した関数のテストを追加しています。

実行結果

pytestを実行しすると、コードに間違いがなければ普通にテストをパスします。

Pythonでpytest fixtureの使い方

今回追加したテスト関数test_saveの一連の流れを見ていきましょう。

calculation.py(メインファイル)

    def save(self, dir_path, file_name):
        if not os.path.exists(dir_path):
            os.mkdir(dir_path)
        file_path = os.path.join(dir_path, file_name)
        with open(file_path, 'w') as f:
            f.write('test')

test_calculation.py(テストファイル)

    def test_save(self, tmpdir):
        self.cal.save(tmpdir, self.test_file_name)
        test_file_path = os.path.join(tmpdir, self.test_file_name)
        assert os.path.exists(test_file_path) is True

pytestでtest_calculation.pyを実行するとまず、test_save()で、メインファイルのsave()が実行されます。

引数によって、メインファイルのdir_pathtmpdirfile_nameself.test_file_name(つまりtest.txt)が代入されます。

このときのtmpdirがpytestのfixtureが自動的に作成した一時的なディレクトリになります。

メインファイルの条件分岐にしているディレクトリはすでに存在しているので、6行目、17行目はスキップされて、ファイルパス(tmpdir)とファイル名(test.txt)をつなげたあと、そのファイルに文字列のtestが書き込まれます。

テストファイルに戻って、メインのファイル同様にファイルパス(tmpdir)とファイル名(test.txt)をつなげたパスが存在しているかをテストしているので、問題なくテストをパスするという流れです。

最後にtmpdirは削除されるので、テスト実行後のプログラムには影響がなくテストができるということです。

流れをつかむ

単純にテストだけをしていれば、どんな場面で使用したらいいのかわからなくなることがあります。

というより、テストになんの意味があるのかもわからなくなってしまいます。

今回のように、テストの流れを追っていくと、fixtureによって自動的にディレクトリが作成され、メインのディレクトリで操作をしたあと、ディレクトリを削除してくれるので、実際のメインファイルの動きが確認できますし、テストが実際の運用環境に影響を与えることがないとわかります。

具体的にどのように使うのかがわかれば、テストへの理解も深まるので、わからないながらも手を動かして頭を使えば、Pythonのスキルが上がること間違いなしです。

夏バテしてしまいそうな気温ですが、しっかり食べてしっかり寝てしっかり学習していこうと思います。

それでは明日もGood Python!