eviry tech & service blog

「株式会社エビリー」の社員ブログです。弊社では、クラウド型動画配信サービス「millvi」、ソーシャル動画データ及び分析サービス「kamui tracker」、YouTube総合メディア「かむなび」を開発・提供しています。https://eviry.com/

Selenium + Capybara + Turnip で始めるテスト自動化

初めまして。開発のukisuと申します。
4月からeviryでエンジニアをやっています。

eviryの開発フローとしてはこれまでブラウザの自動テストは行われていなかったようなので、
前職の経験を基に自動テスト導入の勉強会を実施しました。

この記事はその勉強会で発表した内容をまとめたものです。

目的/メリット

  • テストを行う上で、以下のプロセスを自動化し効率化を目指す
    1. テストケース作成
    2. テスト準備
    3. テスト実行
    4. テスト結果記入
  • なぜ実現できる?
    • テスト実行の命令ファイルの可読性が高く、そのままテストケース仕様書に代用できる
    • テスト実行結果のアウトプットが、そのまま4.として使える

  • 命令ファイルの可読性が高いため、エンジニア以外でも書ける
    • ただし、命令の中身は実装する必要がある
  • これにより、ユーザーに近い立場の者がテストケースを考え、エンジニアは命令の中身を実装する、というような役割分担が可能になる

使用する言語・ライブラリ

言語

Ruby

  • 以下のライブラリを使用する際に環境構築が簡単 & 便利

使用するライブラリ

あまり深い部分を理解していないので、ざっくりな説明になります...。

Selenium Webdriver

  • ブラウザを自動操作してくれるライブラリ
  • 各ブラウザ毎にドライバのインストールが必要

Capybara

  • 一番大事なやつ(多分)
  • Webアプリケーションのインテグレーション・テストを補助する為のライブラリ
  • テスティングフレームワークやブラウザシミュレータに依存せずに、ブラウザ操作の命令を書ける
  • 参考ページ

Turnip

  • 実際のブラウザ操作フローに近い形でテストを記述できるライブラリ
  • なんと日本語でも書けちゃう
  • Gherkin形式というフォーマットで記述
  • 参考ページ

Gnawrnip(読み方分からない...)

  • Turnipの拡張ライブラリ
  • テスト結果を見やすくフォーマットしてくれる
  • Jenkinsとかで定期的にテストする際は重宝するかも
  • 失敗した操作をアニメーションで見せてくれるという神機能

インストール

1. Ruby

Macには最初からRubyが入ってる

https://qiita.com/hujuu/items/3d600f2b2384c145ad12
↑このページに倣って最新版をインストール

次に、bundlerをインストール
bundlerとは、ライブラリ間のバージョン互換性を管理してくれるもの

gem install bundler

# バージョン確認
bundler -v

2. 作業用ディレクトリを作る

mkdir hoge
cd hoge

3. ライブラリ インストール

gemインストール

  • Gemfile作成
bundle init
  • 作成されたGemfileに以下をコピペ
source 'https://rubygems.org'

group :development, :test do
  gem 'selenium-webdriver'
  gem 'gnawrnip' # Capybara,Turnipも自動的にインストール
  gem 'coderay'
end
  • Gemfileで指定したライブラリを一括インストール
bundle install --path vendor/bundle
  • ファイル&フォルダ生成
bundle exec rspec --init
# spec/spec_helper.rb,.rspecが生成される

mkdir spec/steps spec/features

chromedriverインストール

Google Chrome用のSelenium Webdriverドライバ

brew cask install chromedriver

4. 設定ファイル

以下の通りにそれぞれファイルを書き換え

  • .rspec
-r spec_helper
  • spec/spec_helper.rb
# 一番最後に以下を追加
  require 'turnip_formatter'
  datetime = Time.now.strftime("%Y-%m-%d %H:%M:%S")
  config.add_formatter RSpecTurnipFormatter, "output_files/reports/#{datetime}.html"
  config.add_formatter 'progress'
end
  • spec/turnip_helper.rb
Dir.glob("spec/**/*steps.rb") { |f| load f, true }

require 'capybara/rspec'
require 'gnawrnip'
require 'selenium-webdriver'

Capybara.register_driver :selenium_chrome_headless do |app|
    browser_options = ::Selenium::WebDriver::Chrome::Options.new()
    browser_options.args << '--headless'
    browser_options.args << '--disable-gpu'
    browser_options.args << '--window-size=1680,1050'
    Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
end
# Capybara.javascript_driver = :selenium_chrome_headless
# Capybara.default_driver = :selenium_chrome_headless
Capybara.javascript_driver = :selenium_chrome
Capybara.default_driver = :selenium_chrome
Capybara.ignore_hidden_elements = true
Capybara.run_server = false

Gnawrnip.ready!

Selenium::WebDriver.logger.level = :error

5. チュートリアル

以下の2ファイルを作成し、プログラムをそれぞれコピペする

  • spec/features/tutorial.feature
# encoding: utf-8
# language: ja

機能: Seleniumでテストサンプル
  @sample
  シナリオ: 入力フォームの確認
    前提 サンプルサイトへアクセス

    ならば 日付に 1 日後を入力する
    かつ 3 泊を入力する
    かつ 5 人を入力する
    かつ 名前に 'Selenium テスト' を入力する
    かつ 朝食は なし
    かつ プランは 昼からチェックインプラン

    ならば 次へ ボタンをクリック

    ならば 確定 ボタンをクリック

    ならば スクリーンショットを撮る
  • spec/steps/tutorial_steps.rb
# coding: utf-8
require 'date'

steps_for :sample do
    step 'サンプルサイトへアクセス' do
        visit 'http://example.selenium.jp/reserveApp/'
    end

    step ':year 年を入力する' do |year|
        fill_in('reserve_year', with: year)
        sleep 1
    end

    step ':month 月を入力する' do |month|
        fill_in('reserve_month', with: month)
        sleep 1
    end

    step ':day 日を入力する' do |day|
        fill_in('reserve_day', with: day)
        sleep 1
    end

    step '日付に :dayAfter 日後を入力する' do |dayAfter|
        today = Date.today
        inputDate = today + dayAfter.to_i
        fill_in('reserve_year', with: inputDate.year)
        sleep 1
        fill_in('reserve_month', with: inputDate.month)
        sleep 1
        fill_in('reserve_day', with: inputDate.day)
        sleep 1
    end

    step ':term 泊を入力する' do |term|
        fill_in('reserve_term', with: term)
        sleep 1
    end

    step ':headcount 人を入力する' do |headcount|
        fill_in('headcount', with: headcount)
        sleep 1
    end

    step '名前に :guestname を入力する' do |guestname|
        fill_in('guestname', with: guestname)
        sleep 1
    end

    step '朝食は :hasBreakfast' do |hasBreakfast|
        case hasBreakfast
        when 'あり' then
            choose('breakfast_on')
        when 'なし' then
            choose('breakfast_off')
        else
        end
        sleep 1
    end

    step 'プランは :planName' do |planName|
        case planName
        when '昼からチェックインプラン' then
            check('plan_a')
        when 'お得な観光プラン' then
            check('plan_b')
        else
        end
        sleep 1
    end

    step ':buttonName ボタンをクリック' do |buttonName|
        click_on buttonName
        sleep 1
    end

    step 'スクリーンショットを撮る' do
        # 引数には 適当なパス/ファイル名.png
        timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
        page.save_screenshot("output_files/screenshots/" + timestamp + ".png")
    end
end
  • 以下のコマンドを実行!
bundle exec rspec
  • 以下のディレクトリにテスト結果のhtmlファイルが生成されるのでブラウザで確認してみましょう

    • output_files/report
  • 以下のディレクトリには予約完了画面のスクリーンショットが保存されます

    • output_files/screenshots

テスト失敗ケース

  • spec/steps/tutorial_steps.rbを編集して意図的にテストを失敗させてみます
    # tutorial_steps.rb 45~48行目
    step '名前に :guestname を入力する' do |guestname|
        fill_in('guestnameg', with: guestname) # 'guestname' → 'guestnameg'
        sleep 1
    end
  • 先ほどのコマンドを実行すると名前を入力するステップで失敗します
    新しく生成されたhtmlファイルを確認すると、名前を入力するステップにアニメーションが表示されます!

6. テスト実行ファイル作成方法

featureファイル

step定義ファイル

7. ヘッドレスモード

  • ヘッドレスブラウザとは?
    • GUIのないブラウザ、画面表示のないブラウザ
  • ヘッドレスモードでやってみる
    • turnip_helper.rb
Capybara.javascript_driver = :selenium_chrome_headless
Capybara.default_driver = :selenium_chrome_headless
# Capybara.javascript_driver = :selenium_chrome
# Capybara.default_driver = :selenium_chrome
  • テストコマンドを実行すると、画面が立ち上がらず数秒後にテスト終了のメッセージがターミナルに表示されます
    画面は立ち上がりませんが、テスト結果のhtmlファイルも生成され、スクリーンショットもちゃんと写っています!

8. CI連携

  • JenkinsなどのCIツールと連携させることで、設定したトリガーによってテストを自動実行することができる
  • 単体テストでは見抜けないような、思わぬ影響箇所で生じた不具合を見つけることができ、品質を担保することに繋がる

前々職では...

  • Jenkinsと連携し、複数の自動テストを用途に合わせて使い分け
    • 基本的な機能に関するテスト : 短時間で終了するため、ブランチへのマージをトリガーに実行(デプロイも同時に)
    • シナリオテスト : 長時間かかるため、毎週◯曜XX時~というように定期的に実行

まとめ

  • テスト自動化により、ライブラリの整備など導入時のコストはかかるが、テスト工数の削減と品質の担保の両方を実現することができる

以上となります。
これから自動テストを導入する方の参考になれば幸いです!