困る前に！！Seleniumでweb自動テストを導入する際に知っておきたいことまとめ！(前回の応用編になります)

## **はじめに**

前回[Seleniumとtest-unitを使った自動テスト](https://qiita.com/yossyyossy/items/fca76c5647579764418f)を書いたのですが、とてもいい反響をいただき、Qiitaのデイリーに選んで頂きました。

読んでいただいた方ありがとうございます。

今、仕事では自動テストのコードを書いているのですが、リリースによってクリティカルなバグがないよう挙動確認するために使っていただいています。

今回は、僕が何時間も頭を悩ませて見つけた、記事として紹介されていないいろいろな要素の動かし方や特定のブラウザ特有の遅さ、エラーが出てしまう場合の処理、QAプロセスにおいて重要だと感じたことをご紹介したいと思います。

## **作っておくと便利、覚えておいたほうが良いシリーズ**

#### **①select要素とoptionの中身を指定する**


こまったことに、 **普通にfind_elementで要素の指定をしても、何も反応しない。**

select要素専用のインスタンスを作成する必要があります。

面倒なので、専用のメソッドを作成しました。

```ruby

def find_and_select(browser, element_strategy, element_strategy_name, option_strategy, option_strategy_name)

  # セレクト要素を操作するためのインスタンスを作成する。
  select_element = Selenium::WebDriver::Support::Select.new(browser.find_element(element_strategy, element_strategy_name))

  # 選択した要素の値を指定する。
  select_element.select_by(option_strategy, option_strategy_name)

end
```

使い方

```ruby 

# EC等で、商品の購入をテストしたい場合。
# この場合は、商品を1つ選択しているか。
find_and_select(chrome, :id, "item_count", :value, "1")

```

#### **②ラジオボタンと思わせて、checkbox要素**

```html
<form action="なんちゃらかんちゃら" method="post">
  <p>
    <input type="checkbox"id="checkbox" name="one" value="1" checked="checked">メール配信を希望する
  </p>
</form>

```
雑なコードで申し訳ないのですが、このようなHTML要素があったとします。

メールの配信希望等のボタンです。

UIはradioボタンのように見えて、実際はinput要素です。

僕が悩んだところは2つありました。

**・radioボタンなら.clear()を使ってチェックを外すことが出来ることはよく書いてあるはあるけど、input要素でチェックを外す時はどうするの？？どうやって調べても、チェックを付けるときの動作のコードだけ。チェックを外す動作の方法が見つからない。
・選択されている事は.selected?で確認できるけど、選択されていない(checkを外したい)時はどのように確認すればいいんだろうか。**

コチラどのように回避するかといいますと、下記のように書きます。

```ruby

 # メールの配信を希望しないため、選択を外します
 # 要素を選択します。
 this_checkbox = browser.find_element(:id, "checkbox")
 # 該当の要素が選択されているか、確認します。
 if this_checkbox.selected?
   # 選択されていれば、クリックすることでcheckが外れます。
   this_checkbox.click()
 end

```

checkは選択するときだけに使うメソッドだと思っていたのが間違いでした。

こちら、メソッドにすると、このような感じにも出来ます。

```ruby

# conditionという引数を使って、チェックを付ける、外すの動作が両方できるようにしてます。
def find_and_check(browser, strategy, strategy_name, condition)

  # 要素を指定
  this_checkbox = browser.find_element(strategy, strategy_name)

  # 要素を選択されているようにしたいのであれば,selectを引数にする
  if condition == "select"
    this_checkbox.click() unless this_checkbox.selected?

  # 要素を選択されていないようにしたいのであれば,unselectを引数にする
  elsif condition == "unselect"
 
 # 選択されていれば、クリックすることでcheckが外れます。
    this_checkbox.click() if this_checkbox.selected?

end

find_and_check(chrome, :id, "check", "unselect")
```

#### **③モダールウインドウの処理**

javascriptを使って要素を表示させるのがモダールです。

alertと同様に処理する記事が多いですが、input要素のformの場合は、②と同様にclickを使うと上手く行く場合があります。

色々なサービスがあるので全てとは言い切れませんが、こちら困った際に試してみてください。

#### **④要素内の文字列が合っているか検証したい時(assert)**

こちらはどちらかというと、作っておいたほうが便利な内容になります。

```ruby

# 期待する表示と、実際の要素の表示があっているか確認する。

assert_text(browser, expected, strategy, strategy_name)

    # 実際の要素の中身がどのようになっているか確認する。
    # text()を使うことで、実際の文字等を確認することが出来ます
    actual = browser.find_element(strategy, strategy_name).text()

    # 同一かassert_equalを使って確認
    assert_equal(expected, actual)
end
```

## **テストを書き始めた後に困ること**

#### **壊れやすいテストになるのでなるべくcss、xpathで要素指定しないこと**

自動テストを導入したことがない企業では、ilタグやaタグの中にまで名前を振っていない場合があります。

そのため、前回の作業でもcssやxpathを使うことで、そういった要素でも指定できることをご紹介しました。

しかし、cssのセレクタは要素の順番で判別しているため、フロント側のアップデートで要素の位置がずれたりした際に、テストが壊れる可能性があります。

これはよくよく考えてみると、デザイナーの方が悪いのではない(コーディングの段階で関係のない要素までID振る必要がないため)ので、QAからチケット切って依頼するべき案件です。

ただ、すべてのaタグや、trタグtdタグのようなタグにIDを付けていただくのは難しいので、優先順位をつけることで解決しました。

**①：決済周り(金額や購入個数などの部分)**
**②：レイアウトの主要な部分**
**③：各サービスの主要な部分**

こちら、手始めにぜひお話してみてください。

## **メソッドの切り出し**

```ruby 

# chromeでログイン動作

chrome = Selenium::WebDriver.for :chrome
=begin
\
\
\
ログイン動作
\
\
\
=end

# firefoxでログイン動作

firefox = Selenium::WebDriver.for :firefox
=begin
\
\　
\sleep(5)
ログイン動作
\
\
\
=end

# safariでログイン動作

safari = Selenium::WebDriver.for :safari

=begin
\
\
\
ログイン動作
\
\
\
=end
```

このようなプログラムがあったとします。通常であれば、2つ目を作る際に、メソッド化して、このように書くと思います。

```ruby

def login(browser)

=begin
===============
===============
なんちゃらかんちゃから
==============
=end

end

#chromeでログイン動作

chrome = Selenium::WebDriver.for :chrome
login(chrome)

#firefoxでログイン動作

firefox = Selenium::WebDriver.for :firefox
login(firefox)

# safariでログイン動作

safari = Selenium::WebDriver.for :safari
login(safari)

```

しかし、よく見ると、firefoxのコードのみ、ブラウザの挙動を抑えるためにsleepが入っています。

```ruby 
# firefoxでログイン動作

firefox = Selenium::WebDriver.for :firefox
=begin
\
\　
\sleep(5)
ログイン動作
\
\
\
=end
```

メソッドにすると、3回のテスト全てに5秒増えるため、15秒テストが伸びます。

firefoxだけメソッドを使わない方法もありますが、その場合管理コストの高く属人化したメソッドになりますので、僕は秒数が増えてもあえてまとめるようにしています。

これからどのように管理コストを下げつついいテストを書くことができるか、研究していこうと思います。

## 最後に

ご不明な点や、わかりずらい点覚えておいたほうがいいこと等ありましたらぜひ教えてくださいませ。