見出し画像

pytestとは #236日目

pytestとは書いたコードが意図通り動いているか確認するためのもので、開発をしていくなかで必須のスキルです。

以下の動画を見て学ばせていただきました。


まずは単純なテストをやってみます。

srcディレクトリに入っているcode.pyとcode2.pyがテストしたいコードで、testsディレクトリに入っているのがpytestのコードです。

srcは単純な足し算と引き算です。

[src/code.py]
def sum_numbers(a,b):
   return (a + b), a


[src/code2.py]
def diff_numbers(a,b):
   return a - b  

pytestは、テストしたい関数 (sum_numbersとdiff_numbers)を変数に入れて呼び出し、その値を「assert」文で検証します。

[tests/test_code.py]
from src import code

def test_sum_numbers():
   result1, result2 = code.sum_numbers(1,2)
   assert result1 == 3
   assert result2 == 1
   


[tests/test_code2.py]
from src import code2

def test_diff_numbers():
   result = code2.diff_numbers(1, 2)
   assert result == -1
C:\Users\username\tests> pytest

[実行結果]
==================================== test session starts ==================================== 
platform win32 -- Python 3.10.1, pytest-7.0.0, pluggy-1.0.0
rootdir: C:\Users\y.mori\dev\test_python\pytest\tests
plugins: anyio-3.4.0
collected 2 items

test_code.py .                                                                         [ 50%] 
test_code2.py .                                                                        [100%] 

===================================== 2 passed in 0.05s ===================================== 
C:\Users\username\tests> pytest test_code.py

[実行結果]
==================================== test session starts ====================================
platform win32 -- Python 3.10.1, pytest-7.0.0, pluggy-1.0.0
rootdir: C:\Users\y.mori\dev\test_python\pytest\tests
plugins: anyio-3.4.0
collected 1 item                                                                              

test_code.py .                                                                         [100%] 

===================================== 1 passed in 0.01s =====================================


次はやや複雑なテストを実施します。関数の中に関数が入っているものをテストするのですが、その「中の関数」がテスト環境では動かせない、という場合のテスト方法です。

動かせない関数がある場合、その部分の「モック」を作成し、仮のデータで動かします。

以下がテストしたいコードです。

[src/code.py]
import requests

# 以下の関数をモックにする(テスト環境では動かせない関数、という設定)
def get_json_data(id):
   res = requests.get('http://xxxx', params={'id':id})
   res_json = res.json()
   return res_json

def get_user_names(user_ids):
   user_names = {}
   for id in user_ids:
       json_data = get_json_data(id)
       user_names[id] = json_data['name']
   return user_names

以下がpytestのコードです。まずモック部分の関数を作り、そのあとのpytestコードでそのモック関数を呼び出しています。モック関数を呼び出す場合、pytestの関数の引数に「monkeypatch」を渡して、monkeypatch.setattr()というメソッドの中で指定していきます。

テストしたい「get_user_names」がuser_idをリスト形式で受け取ることを想定しているので、ここでは「result」に['001', '009']で渡しています。

[tests/test_code.py]
from src import code

# モック用の関数
def get_json_data_mock(id):
   return {'name': 'YM202110'}

# モックを作るためmonkeypatchという引数を使う
def test_get_user_names(monkeypatch):
   # setattr = set attribute
   monkeypatch.setattr(
       # 第一引数:モックに差し替えたい関数が含まれるモジュール(code)を指定する
       # 第二引数:モックに差し替えたい関数名を文字列で指定する
       # 第三引数:モックの関数を指定する
       code, 'get_json_data', get_json_data_mock)
   result = code.get_user_names(['001', '009'])

   assert list(result.keys()) == ['001', '009']
   assert list(result.values()) == ['YM202110', 'YM202110']

モックで「YM202110」というnameしか渡していないので、それがvaluesに入ってくる想定です。

C:\Users\username\tests> pytest test_code.py

[実行結果]
==================================== test session starts ==================================== 
platform win32 -- Python 3.10.1, pytest-7.0.0, pluggy-1.0.0
rootdir: C:\Users\y.mori\dev\test_python\pytest\tests
plugins: anyio-3.4.0
collected 2 items

test_code.py ..                                                                        [100%] 

===================================== 2 passed in 0.14s ===================================== 


最後に例外処理をpytestで試します。想定した通りの条件で、想定した通りの例外が発生するかテストします。

[src/code2.py]
def user_name_validation(user_name):
   if user_name == None:
       raise ValueError('名前が設定されていません')

例外処理のpytestではwith句を使用します。with句で例外の型を指定すると、その型の例外が発生すること確認するコードになります。以下ではそれに加えて、例外のメッセージが想定通りになっているかもテストしています。

[tests/test_code2.py]
import pytest
from src import code2

def test_user_name_validation():
   # 引数に例外の型を渡すことで、このwith句の中でその例外型の例外が発生するということを確認するテストコードができる
   # ここで定義した例外の型と違う例外が発生したらその時点でエラーが出る
   with pytest.raises(ValueError) as e:
       code2.user_name_validation(None)
   # 例外発生時のメッセージが一緒かどうかを確認するコードにする
   assert str(e.value) == "名前が設定されていません"
C:\Users\username\tests> pytest test_code2.py

[実行結果]
==================================== test session starts ==================================== 
platform win32 -- Python 3.10.1, pytest-7.0.0, pluggy-1.0.0
rootdir: C:\Users\y.mori\dev\test_python\pytest\tests
plugins: anyio-3.4.0
collected 2 items

test_code2.py ..                                                                       [100%] 

===================================== 2 passed in 0.04s ===================================== 


テストを上手く組めるようになることも、開発技術を向上させるうえで重要そうですね。

ここまでお読みいただきありがとうございました!!


この記事が気に入ったらサポートをしてみませんか?