Selenium-WebDriver を使って工数入力チェック業務を自動化

Adways Advent Calendar 2017 1日目の記事です。

http://blog.engineer.adways.net/entry/advent_calendar_2017


こんにちは!まっちゃんです。

さっそくですが、アドベントカレンダーのトップバッターを務めさせていただきます!


さて、本日はある業務を自動化した話を書きます。

現在、私たちのチームでは個人ミッションを掲げています。 自分が掲げた個人ミッションの1つに工数入力チェック業務自動化があります。

毎日自分がどの業務をどれくらい行ったのか、登録するシステムが社内にあります。 基本的に終業時にそのシステムにアクセスをして登録をするのですが、中には忘れる人もいます。 現状は都度チームのマネージャーが確認して、未入力などがあればSlack等でやりとりを行い対応をしています。

これは自動化できるのではないかと、チームのマネージャーであるK先輩にヒアリングしたところ 「入力されているかしか確認をしていない」とのことだったので、 未入力者をSlackへ通知するスクリプトを開発しました。

スクリプトはRubyとSelenium-WebDriverを使いました。

まずはSelenium-WebDriverの導入

今回はGoogle Chromeでどのように動いているのかも見たかったので、下記よりChromeDriverをインストールします。

https://sites.google.com/a/chromium.org/chromedriver/downloads

対象のドライバーをインストールしましたら、 ~/.rbenv/shims/ へ配置します。

コマンドなら下記の感じでダウンロードから、配置までいけると思います。

$ cd ~/tmp
$ wget https://chromedriver.storage.googleapis.com/2.33/chromedriver_linux64.zip
$ unzip chromedriver_linux64.zip
$ cp ~/tmp/chromedriver ~/.rbenv/shims/

Rubyの方では、selenium-webdriver というgemが提供されているので、bundler経由で入れます。

# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "selenium-webdriver"

Gemfileに記述しましたら、インストールします。

$ bundler install --path vendor/bundle

これで準備は完了です。

ログインをどうしようか

工数入力チェックする際に、ログインをしないといけません。

例えば、このようなログインフォームがあったとします。

<form action="/login" method="post">
  <label>id : <input type="text" name="id"></label><br>
  <label>password : <input type="password" name="password"></label><br>
  <input type="submit" value="login">
</form>

このようなRubyスクリプトを書けばログインすることができます。

#!/usr/bin/env ruby

require 'selenium-webdriver'

def main
  driver = Selenium::WebDriver.for :chrome
  driver.navigate.to 'http://localhost/demo/'

  id_element       = driver.find_element(:name, 'id')
  password_element = driver.find_element(:name, 'password')

  id_element.send_keys ENV['USER_ID']
  password_element.send_keys ENV['USER_PASSWORD']
  password_element.submit

  driver.quit
end

main

実行してみます。

$ USER_ID=machaaaan USER_PASSWORD=************ bundler exec ruby script1.rb

実際に簡易のログインフォームを作ってみてやってみました。

f:id:AdwaysEngineerBlog:20171201144821g:plain

ざっくり解説します。

driver = Selenium::WebDriver.for :chrome
driver.navigate.to 'http://localhost/login/'

webdriver経由でGoogle Chromeを立ち上げ、 対象のページ(今回はログインフォームのあるページ)へアクセスします。

id_element       = driver.find_element(:name, 'id')
password_element = driver.find_element(:name, 'password')

フォームの要素を取得します。

id_element.send_keys ENV['USER_ID']
password_element.send_keys ENV['USER_PASSWORD']
password_element.submit

send_keysメソッドで、取得したフォームの要素に対して入力を行います。

今回は環境変数で外に定義したものを入力します。

submitメソッドで入力確定(キーボード入力で言うエンターですね!)をします。

driver.quit

webdriver経由で立ち上げたGoogle Chromeを閉じます。

ちなみに余談ですが、サーバー側で認証を挟んでいる際は下記のように対応します。

driver.navigate.to "http://#{ENV['AUTH_USER']}:#{ENV['AUTH_PASSWORD']}@localhost/top/"

どの要素を取得しようか

工数を登録するシステムの詳しい仕様を知らなかったので、調べてみました。

どうやら、未入力など異常があった場合は違うclass名になるようです。

...
<th class="column">27</th>
<th class="column">28</th>
<th class="alert-column">29</th>
<th class="alert-column">30</th>
...

これは class="alert-column" を取得すればいけそうです。

if driver.find_elements(:class, 'alert-column').size != 0
  alert_elements = driver.find_elements(:class, 'alert-column')
else
  alert_elements = []
end

find_elementsメソッドで指定した要素をすべて取得します。

alert_elementsには、class="alert-column"の要素で取得されたものが格納されています。

str = ''
alert_elements.each do |item|
  str += "#{item.text}"
end

textメソッドで、取得した要素の中にある文字を取得できます。

こうすれば 29日 30日 という形になります。

slackに通知をしよう

今回はwebhookで通知します。 slackのwebhook設定を行い、webhook用のURLを取得します。

Ruby側は slack-incoming-webhooks というgemがあるので、Gemfileに追記します。

# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "selenium-webdriver"
gem "slack-incoming-webhooks" ### [Add]

bundler経由でインストールします。

$ bundler install

Ruby側のスクリプトは下記のようにします。

#!/usr/bin/env ruby

require 'slack/incoming/webhooks'

def main
  slack = Slack::Incoming::Webhooks.new ENV['WEBHOOK_URL'], channel: ENV['SLACK_CHANNEL'], username: 'てすと'

  slack.link_names = 1
  slack.icon_emoji = ':cake:'

  slack.post "スクリプトから通知できとーと?できとーよ! @here"
end

main

環境変数でwebhookURLとチャンネル名を指定します。

実行してみます。

$ WEBHOOK_URL=webhookのURL SLACK_CHANNEL=#test_ch bundler exec ruby script2.rb

下記のようにSlackへ通知されます。

f:id:AdwaysEngineerBlog:20171201140620p:plain

ここまでできたらスクリプト同士を組み合わせていい感じにします。

完成。そして。

個人ミッションの進捗を確認するミーティングが昨日あったので、 チームのマネージャーのK先輩とT先輩に見せました。

好評だったのでさっそくチームのSlackチャンネルに導入することになりました。

下記は実際に本日実行してみた画像です。

f:id:AdwaysEngineerBlog:20171201141442p:plain

cronなどで定期的に回すようにすれば、自動化完了です!

まとめ

初めてSeleniumを触ってみたり、Rubyスクリプトを書いてみましたが、純粋に楽しかったです!

現在はe2eテストなどが盛り上がってきている感じもしているので、

この経験を活かして、既存システムのe2eテスト化などに切り込んでいけたらなと思います。