seleniumテストの失敗、デバッグと引数と解決

seleniumテストが失敗する問題のデバッグと解決
====

# TL;DR
関数名のtypoを修正すると、それまで通っていたテストが落ちる。
原因は、テストシステムが生成して渡した不正な引数による異常系終了だった。
対処として、テストケース実行を汚い実装で検出して特別に処理することで問題を回避することとした。


# 問題
## 詳細
electron製アプリのdaisy-sequenceに対して、selenium/mochaでウィンドウの起動テストを行っている。
これはほぼselenium公式サンプルと同じシンプルなテストである。

daisy-sequenceではコマンドライン引数を受け取る。
コマンドライン引数のパースは`js/cli.js` の `class Cli.parse(argv)`にて行っている。
この中で、`sprintf-js`パッケージの`sprintf()`関数をtypoしていた。
```js
        const sprintf = require('sprintf-js').sprintf;
(略)
        if(2 < argv.length){
            if(argv[2] !== '-o'){
                arg.err = {
                    'message': spirntf('invalid argment. `%s`', argv[2])
                };
                return arg;
            }

```

これを修正したところ、ウィンドウが即座に終了し、seleniumテストに失敗する。
```bash
make unit-test
make[1]: ディレクトリ '/home/nuka/daisy_sequence' に入ります
cd daisy_sequence && npm run test test/

> daisy_sequence@201812.2.0 test /home/nuka/daisy_sequence/daisy_sequence
> mocha --require intelli-espower-loader "test/"



  Array
    #indexOf()
      ✓ should return -1 when the value is not present

  Application launch
    1) shows an initial window
    2) "after each" hook for "shows an initial window"


  1 passing (3s)
  2 failing

  1) Application launch
       shows an initial window:
     chrome not reachable
  Error: Request timed out after the element was still found on the page.
      at windowHandles() - application.js:240:17

  2) Application launch
       "after each" hook for "shows an initial window":
     chrome not reachable
  Error: Request timed out after the element was still found on the page.
      at windowHandles() - application.js:246:17



^CTerminated
Makefile:20: recipe for target 'unit-test' failed
```

# デバッグ

前提として、typoを修正するとテストに失敗する。
別のtypoを入れる、alart()を挿入する等すると、テストに成功するようになる。 // alart()を入れるのは不正なのか?
 > これまでテスト失敗が起こらなかったのは、不正で止まることが原因だったらしい。
arg.errへの代入(arg.errはエラーフラグを兼ねる)を消すとテストに成功する。
 > arg.errへの代入がおかしい? それとも、parse()がエラーを返すと失敗する?
Cli.parse()エラー時の`process.exit(-1);`を消すと、テストに成功する。
 > parse()がエラーを返すとテストが失敗する!
ところでprocess.exit()するとウィンドウが即座に閉じる。ウィンドウが閉じればテストは失敗する。
つまり不正な引数を受け取っていることが原因の可能性がある。

引数を確認する。
console.error()系, process.write.stderr()等を使用しても出力されない。
// 上記デバッグの際もこれが作業の困難さを増した。
対処として、単にファイルに書き出すこととした。
`fs.writeFileSync("daisy-arg", JSON.stringify(remote.process.argv));`
結果、取得できたselenium/mocha実行時のコマンドライン引数。
```js
["/home/nuka/daisy_sequence/daisy_sequence/node_modules/electron/dist/electron","/home/nuka/daisy_sequence/daisy_sequence","--disable-background-networking","--disable-client-side-phishing-detection","--disable-default-apps","--disable-hang-monitor","--disable-popup-blocking","--disable-prompt-on-repost","--disable-sync","--disable-web-resources","--enable-automation","--enable-logging","--force-fieldtrials=SiteIsolationExtensions/Control","--ignore-certificate-errors","--load-extension=/tmp/.org.chromium.Chromium.oi97WO/internal","--log-level=0","--metrics-recording-only","--no-first-run","--password-store=basic","--remote-debugging-port=12106","--test-type=webdriver","--use-mock-keychain","--user-data-dir=/tmp/.org.chromium.Chromium.h85Kss","data:,"]
```
daisy-sequenceは、第二引数が存在する場合に`-o`でなければ、不正なコマンドライン引数として即座に失敗終了する。
(ただし、先頭がelectronで終了する場合これを引数に数えない。)

原因は不正な引数を受け取っていたことによる不正終了により、ウィンドウが即時終了していたことであると考えられる。

## 現象
大筋は以下。
typo修正前の動作はこのようになっていた。
- selenium/mocha実行はelectronに対してテストに適したコマンドライン引数を渡し、アプリケーションはそれを受け取る。
- アプリケーションはtypoにより「未知の関数を実行しようとした例外」で停止する。
- selenium/mocha実行は途中でアプリケーション側に例外が発生しても検知(失敗判定)しない。
- seleniumはウィンドウが起動している・終了できたことに満足してテストをOK判定する。
以上のことからseleniumによるウィンドウ起動テストは成功していた。
なお、「不正な引数を受け取ったことによる不正終了」と「未知の関数を実行しようとした例外」はどちらもアプリケーションの不正終了を起こす。他のテストでは不正な引数をわたしたことによる不正終了が起こりその理由の区別はつかないので、テスト成功となっていた。

typoを修正したことで、以下のように変化した。
- selenium/mocha実行時のコマンドライン引数を受け取ることで、アプリケーションは不正な引数エラーで即座に不正終了する。
- seleniumはウィンドウが起動していないためテストをNG判定する。

// ところで「不正終了」という言葉の使い方が正しくない気がする。まあいいけど。

# 対処
コマンドライン引数からそれらしい引数を検出したらselenium実行と判定してコマンドライン処理をskipすることとした。
キー文字列は"--test-type="にした。

# 今後の課題
- selenium/mocha実行は途中でアプリケーション側に例外が発生しても検知(失敗判定)しないが、検出して失敗判定するようにしたい。
 daisy-sequenceは幸い、正常系で(起動時にwarningが一件出るのと、編集削除操作でエラーログを出力する以外に)例外が発生しない。
- アプリケーション側でパースする際にelectron向けコマンドライン引数を適切に排除する。

技術書典5「クロス本」売り上げ報告ほか

この記事は 技術同人誌 その2 Advent Calendar 2018 Advent Calendar 2018 の17日の記事です。 #技術書典 daisy bell #技術書典5 | 一般 当落状況:当選(き08) 16:00 時点で、事実上の完売というこ...