雑にしか書きませんが、Phoenixのcontrollerのテストでは、あるURLへの操作に対する結果のチェックにはstatus codeが何か、そのtitle要素は何かといった簡単なHTML要素をみます。そこは静的な要素をチェックできるのですが、画面の遷移が絡んでくるとintegration testと呼ばれる段階のテストを用意する必要があります。
Elixirでは、その手のツールに以下の2つがあります。
Wallabyのほうが後発です。これはconcurrentなテストを主眼に開発されているようです。ただ、対応ブラウザがPhantomJSだけだったので、私はHoundを選びました。
そして、concurrent integration testを行うにはphoenix_ecto3.0 + Ecto 2.0を使う必要があります。これは、Ecto2.0から入ったownership制のsandbox環境を使い、concurrentなテストを実行する必要があるためです。
※Ectoのこのownership制の話とか気になる人はこちらを読むと良いと思います。コード
設定
以下の設定説明を参考にすると、基本的なところは完了。サクッと実行できます。なのでここではリンクだけ…
- https://github.com/phoenixframework/phoenix_ecto#concurrent-acceptance-tests
- https://github.com/phoenixframework/phoenix_ecto#hound
私はfirefoxとWebDriverのstandaloneを使ってサクッと実行しました。
WebDriverはこちらからダウンドード可能です=> https://selenium-release.storage.googleapis.com/index.html
tips
通常、この手のintegration testはブラウザを使うのでテスト実行が遅かったり不安定だったりします。そのため、その他のmodelやcontrollerのテストとは別に制御できるようにして、不要なときはskipするなりしたいです。
ここでは、tagを使って制御しましょう。ただ、よくあるメソッドごとに @tag をつけるのでは煩雑になるいっぽうなので、 integration_case.ex なんかを作って、それを読み込んだ全てのモジュールに対して勝手にtagがつくようなやり方です。
tagを付与する
support/integration_case.exにtagを設定する- 以下のようにヘルパーを作ってあげると、
use MyApp.IntegrationCaseしたモジュールは自動的に@moduletag :integrationのtagが付与されます。
- 以下のようにヘルパーを作ってあげると、
| defmodule MyApp.IntegrationCase do | |
| use ExUnit.CaseTemplate | |
| using do | |
| quote do | |
| use Hound.Helpers | |
| import Ecto | |
| import Ecto.Changeset | |
| import Ecto.Query | |
| import MyApp.Router.Helpers | |
| alias MyApp.Repo | |
| # The default endpoint for testing | |
| @endpoint MyApp.Endpoint | |
| # run tests only "integration" mode | |
| @moduletag :integration | |
| end | |
| end | |
| setup tags do | |
| :ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo) | |
| unless tags[:async] do | |
| Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()}) | |
| end | |
| metadata = Phoenix.Ecto.SQL.Sandbox.metadata_for(MyApp.Repo, self()) | |
| Hound.start_session(metadata: metadata) | |
| :ok | |
| end | |
| end |
通常の mix test ではintegration tagを無視して実行する
test/test_helper.exsを以下のようにする
ExUnit.configure exclude: [integration: true] ExUnit.start
この設定を終えた段階で、 mix test とやると、 integration tag がついているモジュールに含まれるテストケースはすべてskipされます。
integration を実行したい
無視されるばかりではなく、integrationだけを実行してみます。
- integration tagだけ
mix test --only integration
- integration tag含めたすべて
mix test --include integration:true
締め
私がプライベート、社内プロダクトで試している限りでは、今の所すべてのテスト(model/controller/integration)は async: true で実行していますが安定してテスト実行されています。
Ecto2.0からのownership制によるDBへのsession制御、賢いですね。ただ、確かあればPostgreSQLはちゃんと動くのですが、MySQLはdeadlockが発生することがあるとか…