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

# はじめに
開発のテストではライブラリやフレームワークを使用して自動化するのが一般的になっています。そこで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についてまとめたもの](https://qiita.com/mink0212/items/a4eb875f19b0e47718d3)を参照してください。

``` 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の単体テストのパラメータをきれいに与える](https://qiita.com/mink0212/items/b26a393a316b533064e3)にまとめました。また、カバレッジの確認方法を[pythonのカバレッジをpytest-covで調べる](https://qiita.com/mink0212/items/34b9def61d58ab781714)にまとめました。