Flaskのflashの内容をpytestでテストする

# はじめに
Flaskアプリの単体テストをpytestを使用して作っていますが、flashの内容をどのように取得したら良いのか不明だったので、調べました。

## 結論
flashの内容はセッションに保存されているので、セッションから取得できます。

## 環境
| ソフトウェア名 | バージョン |
|:-:|:-:|
| Python   |  3.9.0 |
| pytest  |  7.2.1 |
| Flask  | 2.2.2  |

# ディレクトリ構成

```
flask
├ flask_app
│ ├ models
│ ├ templates
│ ├ views
│ │ └ auth.py
│ ├ __init__.py
│ └ ...
└ tests
  ├ conftest.py
  └ test_project.py
```
かなり省略しています。詳細はviews内のauth.py、tests内のconftest.py、test_project_pyの3ファイルにしぼります。

# コード


## \_\_init\_\_.py (Application Factory)

```Python:__init__.py
from flask import Flask

def create_app():
    app = Flask(__name__)
    return app
```

[テストについての公式ドキュメント](https://flask.palletsprojects.com/en/2.2.x/tutorial/tests/)がApplication Factory(create_app()を使用する)の方法に沿って書かれているため、\_\_init\_\_.pyにてcreate_app()を使用してます。

## auth.py (登録後のページ表示)
```Python:auth.py
@auth.route("/signup_done")
def signup_done():
    flash('ユーザー登録を行いました。')
    return redirect(url_for('auth.login'))
```

ユーザー登録画面にて、情報を入力し、「登録」をクリックするとログインページにリダイレクトされ、そこに「ユーザー登録を行いました。」と表示されます。
この表示された内容を確認するテストです。

### conftest.py
```Python:conftest.py
import pytest
from sksk_app import create_app

@pytest.fixture()
def app():
    app = create_app()

    yield app

@pytest.fixture()
def client(app):
    return app.test_client()
```
主に[公式ドキュメント](https://flask.palletsprojects.com/en/2.2.x/tutorial/tests/)を参考にしつつ、以下の動画を参考にして極力シンプルに書いています。
- [Getting Started With Testing in Flask - YouTube](https://www.youtube.com/watch?v=RLKW7ZMJOf4)
- [youtube_video_code/2022/11/30/Getting Started With Testing in Flask at master · PrettyPrinted/youtube_video_code](https://github.com/PrettyPrinted/youtube_video_code/tree/master/2022/11/30/Getting%20Started%20With%20Testing%20in%20Flask)

### テストコード
```Python:test_project.py
def test_signup_post(client):
    client.get('/signup_done')
    with client.session_transaction() as session:
        flash_message = dict(session["_flashes"]).get('message')

    assert "ユーザー登録を行いました。" == flash_message
```
client.get('/signup_done')にて、「/signup_done」にアクセスした際に得られる情報を取得しています。

### セッションの内容の取得 (session_transaction())
session_transaction()は、withステートメントと記述することでセッションの内容を取得できます。
>When used in combination with a with statement this opens a session transaction. This can be used to modify the session that the test client uses. Once the with block is left the session is stored back.
[API — Flask Documentation (2.2.x)](https://flask.palletsprojects.com/en/2.2.x/api/#flask.testing.FlaskClient.session_transaction)

```:session["_flashes"]
[('message', 'ユーザー登録を行いました。')]
```
session["_flashes"]には上記のようなデータが入っているので、dict()で辞書を作成、get()で取得します。キーはmessageとなっていますが、これはflashでカテゴリーを設定していない場合のデフォルトです。

>It is also possible to provide categories when flashing a message. The default category if nothing is provided is 'message'. Alternative categories can be used to give the user better feedback. For example error messages could be displayed with a red background.
[Flashing With Categories - Message Flashing — Flask Documentation (2.2.x)](https://flask.palletsprojects.com/en/2.2.x/patterns/flashing/#flashing-with-categories)


# 参考
- [Test Coverage — Flask Documentation (2.2.x)](https://flask.palletsprojects.com/en/2.2.x/tutorial/tests/)
- [python - Checking flash messages in flask application nose tests - Stack Overflow](https://stackoverflow.com/questions/29508595/checking-flash-messages-in-flask-application-nose-tests)