[flaskr起動&テスト編メモ]HTMLもWeb構築もわからない素人がPython+FlaskでWebサービスを作ってみる

これは、エンジニア未経験のど素人が、終末エンジニアとして
PythonとFlaskでWebアプリを作成するためのメモ記事です。

[ランディングページ](http://qiita.com/yuusei/items/0c202309743214fdce2f)
[Flask](http://qiita.com/yuusei/items/15f38a6abd431d093d2e)
-> [flaskr起動&test編]このページ
[Docker] (http://qiita.com/yuusei/items/51b3917f1ce3f2fbb5e6)
-> [Dockerコマンド](http://qiita.com/yuusei/items/640c85da1a1bf43fe72f)
[Web開発](http://qiita.com/yuusei/items/cf738b0849fc932a64e6)

#起動の手順
## setup.py
さて、[Flask編](http://qiita.com/yuusei/items/15f38a6abd431d093d2e) で一通りコードの説明は終わりなんですが、起動までにはまだ長い(ど素人限定かと思いますが・・・)道のりがあります。

flaskr.py や schema.sql 、 layout.html や login.html だけでなく、setup.py というファイルが必要になります。~~そんな事この[flaskのTutorial](https://flask-docs-ja.readthedocs.io/en/latest/tutorial/#tutorial)には書いてない。まだ途中なのか？~~

setup.pyの中身についてはいちいち書いていてもしょうがないし、このエントリの主眼ではないのでスキップして、とりあえずflaskr以外のアプリも一緒に[GitHubのこのページ](https://github.com/pallets/flask)からdownloadします。

ダウンロードしたら、[flaskrページ](https://github.com/pallets/flask/tree/master/examples/flaskr)にあるREADMEに従って起動します。

## ~ How I use it?
ここではREADMEの中身についてメモして行きます。

### 1. edit the configuration
flaskr.py内のconfigを書き換えて、設定を正しくします(ダウンロードした場合はそのままの設定でとりあえず起動できるのでスキップ可能)

### 2. install the app
Flaskrはパッケージなので[pythonのパッケージ管理システムpipのコマンドオプション “—editable”を使ってローカルパッケージとしてインストールする](http://www.task-notes.com/entry/20150810/1439175600)書式は下記の通り。

> pip install --editable [setup.pyの入っているフォルダpath]

### 3. Instruct flask
正しく使うために、flaskを呼び込んで(読み込んで？)あげる。

>export FLASK_APP=flaskr

正直、ここのコマンドが何をしているのかよく分からない・・・。

### 4. initialize the database
>flask initdb

でDBを初期化する(2回目以降必要)

### 5. now you can run
>flask run

で起動する。
起動した先のLocalhost名は http://localhost:5000/ に準じる。

以上です。このあとTerminal上に表示されたURLをブラウザに入力すると、下記の画面が出るので動作できていることがわかります。

<img width="632" alt="flaskr_01.png" src="https://qiita-image-store.s3.amazonaws.com/0/138270/e707e9a6-784e-810c-4806-65fc281bff55.png">

初めての起動なので、~Unbelievable. No entries here so far~ と表示されます。これは show.html に記載のあった文章ですね。次は実際に動作するかを確かめて行きたいと思います。

#テストしてみた
作成したアプリの動作確認として、ここでは二つの方法をとります。

1. Flask 公式が用意している test_flaskr.py を使用する事による動作確認。
2. 自分の手で実際にアプリを使ってみて確認。

見た目にわかりやすいのは後者だと思うので、まずは後者から行きましょう。

## アプリを実際に使って動作確認してみた
まず、トップ画面の右上にある log inボタンをクリックして、ログイン画面を起動します。

<img width="612" alt="flaskr_02.png" src="https://qiita-image-store.s3.amazonaws.com/0/138270/4f354328-0714-c546-9e95-3e4506fda926.png">


ここで入力するのは、flaskr.py 内にあるapp.config.updateの中で設定した管理者IDとパスです。
このアプリにはサインイン機能は存在していないため管理者IDとパス以外のものを入力すると、エラーメッセージが出ます。

<img width="610" alt="flaskr_03_error.png" src="https://qiita-image-store.s3.amazonaws.com/0/138270/b7603375-8fbc-e6e1-654a-46dc0ee6aea2.png">

管理者IDとパスで入ると、log in したとのメッセージとともに、ブログ投稿欄が出てきます。

<img width="606" alt="flaskr_03_success.png" src="https://qiita-image-store.s3.amazonaws.com/0/138270/ebd0ff2c-bf4b-5974-c319-3f4744ce0c5d.png">


とりあえず入力してみましょう。

<img width="603" alt="flaskr_04.png" src="https://qiita-image-store.s3.amazonaws.com/0/138270/b972ef85-09cc-3059-6aae-e4f6e58973f4.png">

入力した文章はエントリの下に時系列的に出てきます。

<img width="604" alt="flaskr_05.png" src="https://qiita-image-store.s3.amazonaws.com/0/138270/24b66e5e-0513-824f-08b9-2d0fd01dbc72.png">


一通り入力したところで、一旦 log out します。log out してもさっきまで入力したメッセージはそのまま表示されます。

<img width="604" alt="flaskr_06.png" src="https://qiita-image-store.s3.amazonaws.com/0/138270/c3a89c6b-756c-30d5-d417-b4d9b2c56f7f.png">


(注：再度 log in フォームに移ると非表示になりました)

ここで、一旦手元のファイルにどのように反映されているかを探してみました。

>flask initdb

でDBを初期化していたのを思い出してください。
つまり、入力した内容はここに格納されているわけです。

“flaskr.db”ファイルを確認したところ、ありました。一部抜粋すると、下記のような形でDBにデータが格納されていました。

<img width="401" alt="flaskr_07.png" src="https://qiita-image-store.s3.amazonaws.com/0/138270/a429a026-c58b-ede3-4c36-a71d846798a4.png">

これ16進数なんでしょうか？[このサイトに突っ込むと](http://singoro.net/16henkan/)　多少意味のある言葉になって帰ってくるので、間違いではないと思うんですが、入力したものがそのまま返ってきてないのが気になる。どうもsqliteの構文が書かれてる感じ・・・ほかのファイルを一通り見てみても、更新日付的にここなんですよね。なので、一旦ここに入っているという事で進めます。
あとでもう一度 initdb すればわかる話。

追記 1) Initdb したところ、上記と全く同じファイルが作成されていました。つまり、ここには書き込んだ内容は保管されてないわけです。(じゃあどこにあるんだ？？？)

追記 2) 後ほど “Test” “Hello World” だけで再度調査したところ、flaskr.dbから”2354 6573 7448 656c 6c6f 2057 6f72 6c64”を発見しました。ただ、上記の列が記載されている場所よりも大分離れたところに入っていました。ともあれ、ここに格納されているのは確かなので、dbから直接データ取り出したい場合は、[pythonのcodecs](http://qiita.com/atsaki/items/6120cad2e3c448d774bf)と何かを組み合わせて、キーワードを検索するモジュールを作るのがいいかもしれません。

<img width="550" alt="flaskr_08.png" src="https://qiita-image-store.s3.amazonaws.com/0/138270/12e4b047-9d88-11dc-ef9b-7017211a75b3.png">

ちなみに、Terminalには起動してからのすべての動作が記録されます。

<img width="289" alt="flaskr_09.png" src="https://qiita-image-store.s3.amazonaws.com/0/138270/103ae172-429a-75fb-2fef-95135ebbc7df.png">

日付、時間、POSTなのかGETなのかまで丸わかり(怖ッ！)

Ctrl + C で離脱すると、サーバーが落とされ、ローカルホストを入力してもブラウザにflaskrは表示されません。

以上です。

## test_flaskr.py を使用した動作確認
flaskrにはテスト用のpythonスクリプト “test_flaskr.py” があり、そのコピーライトはArmin Ronacher氏が保持しています。

テストの内容は作りたいアプリケーションによって変わりますが、
flaskr において必要なテストは「ログイン/アウト」のテストと「メッセージ表示」のテストになります。


### ログイン/アウトテスト
test_flaskr.py の中に、 test_login_logut() 関数が定義されています。
中身を見ていきましょう。

>def login(client, username, password):
>    return client.post('/login', data=dict(
>        username=username,
>        password=password
>    ), follow_redirects=True)
>
>
>def logout(client):
>    return client.get('/logout', follow_redirects=True)
>
>  ・・・(省略)・・・
>
>def test_login_logout(client):
>    """Make sure login and logout works"""
>    rv = login(client, flaskr.app.config['USERNAME'],
>               flaskr.app.config['PASSWORD'])
>    assert b'You were logged in' in rv.data
>    rv = logout(client)
>    assert b'You were logged out' in rv.data
>    rv = login(client, flaskr.app.config['USERNAME'] + 'x',
>               flaskr.app.config['PASSWORD'])
>    assert b'Invalid username' in rv.data
>    rv = login(client, flaskr.app.config['USERNAME'],
>               flaskr.app.config['PASSWORD'] + 'x')
>    assert b'Invalid password' in rv.data

前半は login/logutの関数を定義して、後半で実際に”username”と”password”を利用したテストが行われています。
問題なければ “OK” のメッセージがTerminalに出力されます。

### メッセージ表示テスト
さっきはlogin 関数のクラスを追加しましたが、
こっちは test_message() 関数を設定するだけです。

>def test_messages(client):
>    """Test that messages work"""
>    login(client, flaskr.app.config['USERNAME'],
>          flaskr.app.config['PASSWORD'])
>    rv = client.post('/add', data=dict(
>        title='<Hello>',
>        text='<strong>HTML</strong> allowed here'
>    ), follow_redirects=True)
>    assert b'No entries here so far' not in rv.data
>    assert b'&lt;Hello&gt;' in rv.data
>    assert b'<strong>HTML</strong> allowed here' in rv.data

ここではタイトルを「Hello」、本文を「<strong>HTML</strong> allowed here」として、
さっきもあった assert というのはpythonにある[デバッグ用のコード](http://cortyuming.hateblo.jp/entry/20081208/p3)らしいです。例外を故意に発生させる為のものらしいです。

書式は
> assert [論理文], “error時の表示条件(オプション)
であり、例えば test_message() の下から二番目の一文では、
「Helloがrv.dataの中にバイナリで格納されているか」をチェックしています。

この二つ以外のテストについては、別途テスト用の関数を書き下す必要があります。
このようにして、テスト関数が用意できたら、実際にテストして見ます。

### 書いたテストコードを走らせる。
Flaskrの”README”においては、

>    ~ Is it tested?
>
>      You betcha.  Run `python setup.py test` to see
>      the tests pass.

「テストしましたか？」
「もちろんだよ！”python setup.py” testを走らせて確認したさ！」

とありますので、これを走らせれば大丈夫です。

Terminal上で実際に走らせて見ると、こうなります。

>[workingdirectry]$ python setup.py test
>running test
>running egg_info
>writing flaskr.egg-info/PKG-INFO
>writing dependency_links to flaskr.egg-info/dependency_links.txt
>writing requirements to flaskr.egg-info/requires.txt
>writing top-level names to flaskr.egg-info/top_level.txt
>reading manifest file 'flaskr.egg-info/SOURCES.txt'
>reading manifest template 'MANIFEST.in'
>writing manifest file 'flaskr.egg-info/SOURCES.txt'
>running build_ext

>----------------------------------------------------------------------
>Ran 0 tests in 0.000s

>OK


あれ？テストしてないですね。
なんででしょうか？


続きます。