Elixir
Phoenix

Phoenixでインテグレーションテストをする

More than 1 year has passed since last update.

やりたいこと/やること

Elixir/PhoenixでSessionを使用した画面遷移を伴うインテグレーションテストをしたい
PhoenixデフォルトではSessionを扱ったテストを書くのが少し面倒なようなので、今回はインテグレーションテスト用のライブラリでPhantomJS経由でテストをします

ここらへん公式でドキュメントもっと充実するかデフォルトで簡単にインテグレーションテスト出来るといいのになー

環境

  • Phoenix 1.1.4
  • Elixir 1.2.5
  • hound 1.0.0
  • PhantomJS 2.1.1
  • Docker 1.11.1

手順

  • ElixirのIntegration Testライブラリのhoundをmixの依存に追加

mix.exs

{:hound, "~> 1.0"}

依存を更新

mix deps.get
  • ローカルにPhantomJSを入れるのが面倒なのでリモートサーバとしてPhantomJSをDockerで起動する
docker run -itd -p 8910:8910 shufo/phantomjs --webdriver 8910

docker-composeを使っているならサービスをdocker-compose.ymlに追加する

  phantomjs:
    image: shufo/phantomjs
    container_name: phantomjs
    hostname: phantomjs
    command: "--webdriver 8910"
  • houndの設定

config/config.ex

config :hound, driver: "phantomjs", host: "phantomjs", port: 8910

補足: hostは起動したphantomjsのホスト名。docker-composeで起動した場合は自動的にservice名で解決出来るのでphantomjsでok。docker-composeを使わない場合はDockerデーモンを起動しているマシンのホスト名かdocker inspect等でコンテナのIPを確認。

test_helpers.exs

Application.ensure_all_started(:hound)
ExUnit.start

ExUnit.startの前にApplication.ensure_all_started(:hound)を追加

config/test.exs

config :example, Example.Endpoint,
  http: [port: 4001],
-  server: false
+  server: true

MIX_ENV=test時のEndpointのconfigをserver: trueに変更

  • Testを書く

test/controllers/auth_controller_test.exs

defmodule Example.AuthControllerTest do
  use Example.ConnCase
  import Example.Router.Helpers
  use Hound.Helpers

  # Start hound session and destroy when tests are run
  hound_session

  @moduletag :auth

  setup do
    # ユーザ作成処理等
    # ~中略~

    navigate_to("/auth/login")
    assert page_source =~ "ログイン"

    username = "foobar"
    find_element(:name, "username")
    |> fill_field(username)

    find_element(:name, "password")
    |> fill_field("123456")

    find_element(:tag, "button")
    |> click

    assert page_source =~ "ようこそ"
    {:ok, username: username}
  end

  test "user can logout", %{username: username} do
    # click logout
    find_element(:id, "button_get_logout")
    |> click
    # Assert redirected to login page
    assert page_source =~ "ログイン"
    assert current_path == auth_path(conn, :get_login)
    # Assert logouted user can't access to private page
    navigate_to("/mypage")
    assert current_path == auth_path(conn, :get_login)
  end
end

補足: setup内で共通処理をしてログイン。HoundのHelperのfind_element/2で要素を選んで値を入れたり出来る。

  • Test実行
mix test --only auth

@moduletagでテストの実行範囲を絞れる

これで一通りインテグレーションテストが出来た。

find_element/2で指定できるelementの指定の方法は他にも:name, :tag, :id, :class, :css, :link_text等がある。
fill_element/2で選択した要素にinput valueを入れてclick/1submit/1でフォームをsubmitしてcurrent_path/0current_url/0で遷移先のパスが正しいか確認。あとpage_source/0でページに期待する文字列が含まれるかチェック、辺りが出来るので基本的なところは十分だと思うけど足りない場合は Hexのdocument を参照してください。

追記:

少しやり方が特殊なテストでハマったので追記

  • ファイルアップロード

input_into_field/2に対してファイルのフルパスを入力する

find_element(:name, "image")
|> input_into_field("#{__DIR__}/../../fixtures/example.jpg")

ファイルはphantomjsが実行されている環境のローカルファイルシステムを参照するのでDockerで起動している場合はコンテナに必要なファイルをマウントしておく

例)

docker run -itd -p 8910:8910 -v $(pwd):/app shufo/phantomjs --webdriver 8910
  phantomjs:
    image: shufo/phantomjs
    container_name: phantomjs
    hostname: phantomjs
    command: "--webdriver 8910"
+    volumes:
+      - ".:/app"

カレントディレクトリを/appにマウントするのでinput_into_fieldでは/app/test/fixtures/example.jpgなどで参照出来る

  • セレクトボックスの選択
find_element(:css, "#element_id option[value='value']")
|> click