SIerに入って８年ぐらい経ったのでいろいろ棚卸しをしてみる。【vol.008 Selenium】

## 出会い

Seleniumとの出会いは、2013年頃にJavaの案件をやっていた際のテストで「何度も同じ操作するのイヤ」という怠け心がきっかけとなっている。確かテストデータ作成か何かだったと思う。

その後もちょっとした画面テスト自動化やテストデータ作成でSelenium使ったり、「ブラウザ操作するよりコード書いてSeleniumに任せた方が楽！」という場合にお世話になっている。

## Seleniumとは
>Selenium は、Web ベースアプリケーションのテスト自動化の高速開発をサポートした堅牢なツール群です。Selenium は、Web アプリケーションのテストニーズに特化したテスト機能を豊富に備えています。テスト操作の柔軟性は高く、UI 要素を特定したり、テストの期待値と実際のアプリケーションの動作を比較したりするための多数のオプションを利用できます。
>[Selenium とは](http://oss.infoscience.co.jp/seleniumhq/docs/01_introducing_selenium.html#)

事前に指定されたブラウザ操作を自動で実行してくれる技術です。Java・C#・Pythonなどでコードとして操作内容を記述することも出来ますし、Firefoxだと拡張アドオンを入れれば実際操作した内容を記録することも出来ます。

似たような技術としては、[Excelマクロ](https://support.office.com/ja-jp/article/%E3%83%9E%E3%82%AF%E3%83%AD%E8%A8%98%E9%8C%B2%E3%81%A7%E4%BD%9C%E6%A5%AD%E3%82%92%E8%87%AA%E5%8B%95%E5%8C%96%E3%81%99%E3%82%8B-974ef220-f716-4e01-b015-3ea70e64937b)が挙げられると思います。定型的で何度も同じ操作が繰り返されるケースで効果を発揮します。システム開発だとテストフェーズで使用される技術かと思います。

ブラウザ操作自動化という意味だと最近、RPAとか流行っているので事務作業とかが該当するのかもしれません。

## 振り返ってみて

個人的には、ちょっとした画面テスト自動化やテストデータ作成で使うぐらいがちょうどいい技術だと思っています。

ちょっとした画面テスト自動化というのは、テスト工程すべてを自動化するのではなくて、

 - 入力する項目が多い最初の画面で、一通り入力を終えるところまで自動化する。
 - テスト対象の画面にたどり着くまでのログイン→メニュー選択→画面遷移までを自動化する。

といった部分的な操作です。

すべてを自動化するのは、SeleniumHQのページにも書いてあるように[自動化するか、しないか、それが問題だ](http://oss.infoscience.co.jp/seleniumhq/docs/01_introducing_selenium.html#)と経験上思います。画面デザインが変われば、すぐに動かなくなるので、画面の機能実装中に作るのは適切ではないと思います。

じゃあ、画面デザイン確定後に作ればいいかというと、そうでもないと思います。メンテンスコストに見合うだけの効果があると判断された場合だけ作られるべきだと考えています。実際にテストシナリオ丸ごと作ったはいいけど、メンテンナンスをしている余裕などなく、放置されるケースもよく見てきました。

そういったところで、あまりコストをかけずにササッとSeleniumで用意できるレベルで、ちょっとした画面テスト自動化やテストデータ作成で使うのがいいと思います。

テストデータ作成については、SQLで用意しておければいいのですが、以下のようなケースは画面から操作したデータの方が整合性が取れているし、実際そういったケースで使ってきました。

 - 関連テーブルが多くてデータの依存関係が多い。
 - 画面上で選択した内容によってデータパターンが色々生まれる。


## サンプルで実装してみる。
こちらの記事を参考にさせていただきました。

- [はじめての C# からの Selenium、そして NET Core ではエラーになる場合](https://devadjust.exblog.jp/27257580/)
- [Selenium with C# (Waiting)](https://gist.github.com/up1/d925783ea8e5f706f3bb58c3ce1ef7eb)
- [Selenium webdriverよく使う操作メソッドまとめ](https://qiita.com/mochio/items/dc9935ee607895420186)
- [Html Agility Pack](https://html-agility-pack.net/)
- [Seleniumで要素を選択する方法まとめ](https://qiita.com/VA_nakatsu/items/0095755dc48ad7e86e2f)

Nugetでインストールしたパッケージは、「Selenium.WebDriver」「Selenium.WebDriver.ChromeDriver」「HtmlAgilityPack」です。

```c#
using HtmlAgilityPack;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System;

namespace SeleniumSample
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var webdriver = new ChromeDriver())
            {
                //描画が追いつかなくて要素が見つからないことが多々あるので、待機する設定。
                var fluentWait = new DefaultWait<IWebDriver>(webdriver);
                fluentWait.Timeout = TimeSpan.FromSeconds(10);
                fluentWait.PollingInterval = TimeSpan.FromMilliseconds(500);
                fluentWait.IgnoreExceptionTypes(typeof(NoSuchElementException));

                //EDINETのサイトへアクセスする。
                webdriver.Navigate().GoToUrl("https://disclosure.edinet-fsa.go.jp/E01EW/BLMainController.jsp?uji.bean=ee.bean.parent.EECommonSearchBean&uji.verb=W0EZA230CXP001007BLogic&TID=W1E63010&PID=W0EZ0001&SESSIONKEY=&lgKbn=2&dflg=0&iflg=0");

                //検索キーワード入力要素を見つける。
                var edinetInputElement = fluentWait.Until(x => x.FindElement(By.Id("mul_t")));

                //SONYを入力する。
                edinetInputElement.SendKeys("SONY");

                var edinetButtonElement = fluentWait.Until(x => x.FindElement(By.Id("sch")));

                //検索ボタンをクリック。
                edinetButtonElement.Click();

                //検索結果のHTMLを取得
                var html = webdriver.PageSource;

                //パースする。
                var document = new HtmlDocument();
                document.LoadHtml(html);

                //指定日（例えば今日が「H31.02.13」）のドキュメントがあるかどうか確認する。
                var hasTodayDocument = false;
                foreach (var node in document.DocumentNode.ChildNodes) {

                    if (node.InnerText.Contains("H31.02.13")) {
                        hasTodayDocument = true;
                        break;
                    }
                }

                Console.WriteLine(hasTodayDocument ? "Exist!" : "Nothing!");

                //あった場合は、Google検索してニュースを探す。
                if (hasTodayDocument) {
                    webdriver.Navigate().GoToUrl("https://www.google.com/?hl=ja");

                    var googleInputElement = fluentWait.Until(x => x.FindElement(By.Name("q")));

                    googleInputElement.SendKeys("SONY トップニュース");
                    googleInputElement.SendKeys(Keys.Enter);
                }
                

                Console.ReadKey(intercept: true);
            }
        }
    }
}

```

４月に[EDINETのAPI](https://disclosure.edinet-fsa.go.jp/EKW0EZ0015.html)公開されたので公式を使うのが楽かと思いますが、こんなことも出来ますというサンプルです。
特定の機能を使ったあと、その結果（HTML）次第で次のアクションを行う、といった連続的な操作もSeleniumで可能です。