43
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

pytestでflaskの単体テストをする

Last updated at Posted at 2020-02-22

はじめに

開発のテストではライブラリやフレームワークを使用して自動化するのが一般的になっています。そこでpytestを使用してflaskの単体テストを自動化しようとしましたが、シンプルな例が見つけられなかったのでシンプルな例と簡単な説明をまとめました。

環境

  • python:3.6.5
  • flask:1.0.2
  • pytest:5.3.5

インストール

pip install pytestでインストールするだけです。

pytestで自動化するのに必要なもの

pytestで単体テストを自動化するために必要なものは、テスト対象のソース(テストされる開発物)とテスト方法を記載したソースが必要になります。テスト方法のソースは、テスト対象の引数と関数の結果を与えてどのように比較するかを記載しています。

簡単な関数の単体テスト自動化

flaskの単体テストの自動化の前に、簡単な関数を通じてpytestの使い方を見ていきます。

テスト対象のソース

テスト対象のソースが無ければテストはできないため、テスト対象のソースを用意します。
例では、引数を加算して返却する関数を用意しましたが、本来の開発であれば開発物が相当します。

testing_mod.py
def add_calc(a, b):
    return a + b

テスト方法を書いたソース

テスト対象のソースの呼び方とソースの結果を書いたソースを作ります。このソースはテスト対象のソースの関数を呼び出して、テスト対象の関数が返した結果とこちらが想定した結果を比較して正しければOK、誤っていればNGになります。
例では、テスト対象のtesting_modをimport testing_mod でimportしてtesting_mod.add_calc()に1と2を渡して返却される結果が3であればOKとなっています。

py_test_main.py
import pytest
import testing_mod

def test_ok_sample():
    result = testing_mod.add_calc(1, 2)
    assert 3 == result

単体テスト実行

テスト対象とテスト方法のソースができたため、関数ごとに結果を見たいため-vオプションをつけて実行します。

# pytest -v py_test_main.py

py_test_main.py .         [100%]                                                                                                                                       
====== 1 passed in 0.05s ======
PS C:\Users\xxxx\program\python> pytest -v py_test_main.py                                   
====== test session starts ======
platform win32 -- Python 3.6.5, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 -- c:\users\xxxx\appdata\local\programs\python\python36-32\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\xxxx\program\python
collected 1 item                                                                                                                     
py_test_main.py::test_ok_sample PASSED     [100%] 

====== 1 passed in 0.02s ======

結果を見ると先ほど作成したtest_ok_sampleがPASSEDとなり正常に終わったため、テストOKになります。
関数をたくさん作成すると表示される関数の数が増えていきます。

簡単なflaskの単体テスト自動化

flaskの単体テストを自動化します。上の簡単な関数の自動化とは異なり、flaskはクライアントからの通信を必要としますが、単体テストではflaskの機能を使用して単体テストの自動化をします。

テスト対象のソース

flaskのソースを作成します。例では/にアクセスするとroot文字列を返却するものを作成します。本来の開発であれば開発物が相当します。
flaskについては以前のflaskについてまとめたものを参照してください。

flask_mod.py
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def root():
    return "root"

テスト方法を書いたソース

flaskのテスト方法を書いたソースは、関数のソースと異なりflaskのクライアントを生成してからそのクライアントを使用してリクエストを発行して結果を確認する必要があります。

テスト用flaskクライアントの生成

まずは、テスト用のクライアントの生成を行います。テスト対象ソースのappをimportしてappのテスト用configをtrueに変更します。その後appのtest_client()を使用してクライアントを生成します。
下の例でいうとテスト対象のソースのimportはfrom flask_mod import appになります。

py_test_main.py
import pytest
from flask_mod import app

def test_flask_simple():
    app.config['TESTING'] = True
    client = app.test_client() 

テスト対象の関数の実行

上で生成したクライアントを使用して、テスト対象のURLに向けてget関数やpost関数を使用してリクエストを発行します。その結果がflaskからのレスポンスになるため、期待した答えになるかをpytestのassertでチェックします。
下の例でいうとresult = client.get('/') で/にgetリクエストを発行してその結果がresultに格納されるのでdata(body)とrootを比較しています。

py_test_main.py
import pytest
from flask_mod import app

def test_flask_simple():
    app.config['TESTING'] = True
    client = app.test_client() 
    result = client.get('/')
    assert b'root' == result.data

単体テスト実行

テスト対象とテスト方法のソースができたため、実行します。

# pytest -v py_test_main.py

====== 1 passed in 0.22s =======
PS C:\Users\xxxx\program\python\flask> pytest -v .\pytest_flask.py
====== test session starts ======
platform win32 -- Python 3.6.5, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 -- c:\users\xxxx\appdata\local\programs\python\python36-32\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\xxxx\program\python\flask
collected 1 item

pytest_flask.py::test_flask_simple PASSED  [100%]

====== 1 passed in 0.20s =======

結果を見ると先ほど作成したtest_flask_simpleがPASSEDとなり正常に終わったため、テストOKになります。
関数をたくさん作成するとここの関数の数が増えていきます。

単体テストがエラーの時の例

試しに、flaskが返却してくる文字列と比較する文字列をsampleにしたときの結果を見てみます。

# pytest -v pytest_flask.py
======= test session starts =======
platform win32 -- Python 3.6.5, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 -- c:\users\xxxx\appdata\local\programs\python\python36-32\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\xxxx\program\python\flask
collected 1 item

pytest_flask.py::test_flask_simple FAILED                                                                                                                          [100%]

============ FAILURES ============= 
____________ test_flask_simple ____________

    def test_flask_simple():
        app.config['TESTING'] = True
        client = app.test_client()
        result = client.get('/')
>       assert b'sample' == result.data
E       AssertionError: assert b'sample' == b'root'
E         At index 0 diff: b's' != b'r'
E         Full diff:
E         - b'sample'
E         + b'root'

pytest_flask.py:8: AssertionError
======== 1 failed in 0.26s ========

ちゃんとsampleとrootが異なるのでAssertionError: assert b'sample' == b'root'と表示されました。

おわりに

単体テストの自動化は、自動化スクリプトの作成の労力が少なければとても便利な仕組みになります。
その労力を少なくする方法がフレームワークですが、上記以外にも、テストの前処理と後処理をする、同じテスト方法で複数のパラメータを試すなど便利な機能があります。次はその便利な方法をまとめていきます。 便利な方法をpytestでflaskの単体テストのパラメータをきれいに与えるにまとめました。また、カバレッジの確認方法をpythonのカバレッジをpytest-covで調べるにまとめました。

43
35
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
43
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?