CodeceptJSとPuppeteerでE2Eテスト（データ駆動、並列実行）

#初めに
CodeceptJSとPuppeteerを使ってE2Eテストを書いてみました。
コードの書き方も独特ですが便利な機能が多いと思います。

途中詰まってしまった部分もあり、それらを含めて備忘録的にまとめていきます。
内容に間違いがあればご指摘ください。

#目次
- CodeceptJSとは
- 準備
- テストコードを書く
    - コード詳細
        - I.amOnPage
        - I.fillField
        - I.pressKey
        - I.wait
        - I.grabTextFrom
        - I.see
    - 躓きポイント
- スクリーンショットで記録
- データ駆動してみる
- 並列実行してみる
- テストレポートを見る
- 終わり
- 参考

##CodeceptJSとは
まずCodeceptJSとはなんなのか。
最後に記載しました参考サイトにわかりやすく書いてありました。

ざっくりいうとCodeceptJSはNode製のE2Eテストフレームワークで、
いろいろなブラウザ操作ライブラリと組み合わせることでブラウザテストを実行します。

ここでおお、と思ったのは
CodeceptJSの独特な書き方を使えばWebdriverIO(Selenium)、Appium、Nightmare、Puppeteerなどのいろんなツールでコードを使いまわすこともできる、ということです。

今回は複数あるツールの中で、手軽で実行速度も速いというPuppeteerを使ってテストをしてみました。

##準備
・Node.js v8.9以上
・npm　　　　　　　　　が必要です。

あとはCodeceptJSとPuppeteerをインストールして、
初期設定でいろいろ聞かれるのでそのときに使いたいツールを選択します。

```
npm install codeceptjs puppeteer --save-dev
npx codeceptjs init 
```

設定の細かな部分はこちらのサイトが分かりやすかったです

https://qiita.com/takeh/items/8ea694c6e8513b062210#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB--%E5%88%9D%E6%9C%9F%E8%A8%AD%E5%AE%9A

##テストコードを書く
初期設定でテストファイルの記載も設定していると思います（デフォルトは./*_test.js）
その書き方でファイルを作成し、コードを書いていきます。

ブラウザを立ち上げ、検索欄に入力して表示内容を確認するテストから作成していきます。
↓↓こんな感じのコードになりました。

```
Feature('はじめてのCodeceptJS').retry(1);

Scenario('Googleで検索、表示内容の確認', async({ I }) => {
    I.amOnPage('https://www.google.com');
    I.fillField('q', 'CodeceptJS');
    I.pressKey('Enter');
    I.wait(1);
    let firstResult = await I.grabTextFrom('h3');
    I.click(firstResult);
    I.fillField('#algolia-search-input', 'puppetter');
    I.pressKey('Enter');
    I.see('Now you can use Puppeteer for Firefox (min version: Firefox/63.0.4)');
});
```

動かすときは--stepsをつけると細かく結果がでてくれます。
ファイル名を付ければそのファイルのみの実行も可能です。

```
npx codeceptjs run --steps
```

###コード詳細

```
Feature('はじめてのCodeceptJS').retry(1) 
```

.retry(1) という記載をしています。
これは全てのシナリオに対して失敗した場合は1回までリトライをしてくれる機能です。
1度失敗したテストもリトライして成功すればそれは成功とみなされます。


```
I.amOnPage('https://www.google.com');
I.fillField('q', 'CodeceptJS');
I.pressKey('Enter');
I.wait(1); 
let firstResult = await I.grabTextFrom('h3');
```

上記コードの説明をしていきます。
↓↓詳細は公式にわかりやすく書いてますのでそちらをご参照ください。

https://codecept.io/basics/#writing-tests


####I.amOnPage
ex) I.amOnPage('開きたいページ')
URLがプロトコル（http：//またはhttps：//）で始まらない場合は相対URLと見なされます

####I.fillField
ex) I.fillField('入力したい場所', '入力内容')
入力場所はラベル、name、CSS、XPathで指定できます。
ラベルでも指定ができるのでどこに入力したいのかが分かりやすいです。
複数ヒットしてしまい、うまく探せないときはwithin()で絞り込む必要がありますがここでは詳細を省きます。

####I.pressKey
ex) I.pressKey('Enter')
'Alt'、'Control'、'Shift'などいろいろ対応してます。

####I.wait
ex) I.wait(1)
待機したい秒数を書きます。

####I.grabTextFrom
ex) I.grabTextFrom('h3')
CSSまたはXPathによって配置された要素からテキストを取得し、テストに返します。
テストの実行を再開するためにasync/awaitが必要です。
複数の要素が見つかった場合、最初の要素を返します。

####I.see
ex) I.see('確認したいテキスト')
ページに表示されているテキストが含まれていることを確認します。
コンテキストパラメータを使用して検索を絞り込むこともできます。
ex) I.see('確認したいテキスト', '.content');

###躓きポイント
私は2点で引っ掛かりました。1つは↓の記載です。

```
Scenario('Googleで検索、表示内容の確認', async({ I }) => {
```
今回CodeceptJSのバージョン3を使用しているのですが、参考にしたサイトのバージョンが古いことに気が付かず

```
async( I ) => {
```
上記で記載していたところエラー
この書き方のバージョン古いですよ、みたいな内容のエラー文が出たので探したところこれが原因でした。

もう一つはgrabTextFromの使い方です。
async/awaitが必要になるところを書かずに動かそうとしてエラー
調べたら思いっきりサイトに書いてありました。反省。

https://codecept.io/helpers/WebDriver/#grabtextfrom

##スクリーンショットで記録
CodeceptJSでは動作ごとにスクリーンショットをとる設定ができます。

```
//codecept.conf.js

plugins: {
  stepByStepReport: {
      enabled: true,
      deleteSuccessful: false,
　}
}
```

上記の追記だけでできるので簡単で有難いです。

任意のタイミングでのみ取りたいときにはsaveScreenshotでとることができます。

##データ駆動してみる
キーワードを入れて検索した結果を確認するテストです。
testWordsにそれぞれ検索ワードと確認したい内容をいれてます。

```
const testWords = [
    { 'searchWord': 'puppetter', 'responseWord': 'Now you can use Puppeteer for Firefox (min version: Firefox/63.0.4)' },
    { 'searchWord': 'selenium', 'responseWord': 'Testing with WebDriver' },
]

Data(testWords).Scenario('データ駆動テスト', async({I, current}) => {

  I.amOnPage('https://www.google.co.jp/')
  I.fillField('q', 'CodeceptJS');
  I.pressKey('Enter');
  I.wait(1);
  let firstResult =await I.grabTextFrom('h3');
  I.click(firstResult);
  I.fillField('#algolia-search-input', current['searchWord']);
  I.pressKey('Enter');
  I.see(current['responseWord']);
})
```

そのtestWordsをData().Scenarioに入れてあげるとデータの分繰り返しテストします。

```
Data(testWords).Scenario('データ駆動テスト', async({I, current}) => {
```

ここでcurrentにtestWordsのデータが入るようです。
今回私はcurrent['responseWord']、の書き方で書いてますが
公式を見た感じはcurrent.login、のように書いてます（なぜかうまくいかなかった）


二つしかデータがないのであれですが↓のように
書いた方が良かった気もしています。

```
//サイトから抜粋
let accounts = new DataTable(['login', 'password']); //
accounts.add(['davert', '123456']); // adding records to a table
accounts.add(['admin', '123456']);
```

https://codecept.io/advanced/#data-driven-tests


##並列実行してみる
CodeceptJSは並列実行も簡単でした。

```
//codecept.conf.js

multiple: {
    parallel: {
      chunks: 3
    }
}
```

```
npx codeceptjs run-workers 3
```

codecept.conf.jsに追記して上記で実行するとブラウザが三つ立ち上がって処理が進みます。

##テストレポートを見る
CodeceptJSにプラグインをインストールすることでこんな感じのレポートを出力することができます。

![codeceptjs-report.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1733797/180a0d2c-e018-7868-d187-8182458a6b73.png)

```
npm install -g allure-commandline --save-dev
```

```
//codecept.conf.js

"plugins": {
    "allure": {}
}
```

```
npx codeceptjs run --plugins allure
allure serve output
```

方法はこれまた公式に書いてある通りです。とりあえず公式サイトを調べれば何とかなるのでは。
インストール、codecept.conf.jsに追記、プラグインを有効にしてテスト実行、Allureサーバーを起動、の流れです。
テストを実行した際にできたレポートがoutputディレクトリ（デフォルトの場合）に保存されます。

#終わり
便利な機能が多く、設定も簡単でした。

今回触っていないプラグインにも便利そうなものがまだまだあり、
・Cookieをファイルに保存し、セッションを再利用できるプラグインでログイン処理を省略
・実行されたテストごとにビデオを録画および保存（※Dockerが必要）
なんてことも可能なようです。

Jenkinsとの連携もできるようですが、私には少し解読が必要そうです。

https://codecept.io/continuous-integration/#recipes

触っていて楽しかったでのもう少し深堀していきたいと思ってます。
間違いなど何かあればご教示ください。

#参考
https://codecept.io/basics/#architecture

https://qiita.com/tsuemura/items/70b156baecfbcffb03fb#%E3%82%B9%E3%83%86%E3%83%83%E3%83%97%E3%81%94%E3%81%A8%E3%81%AB%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%E3%82%92%E5%8F%96%E3%82%8B

https://qiita.com/Kesin11/items/11da1b4cf9d58e4dd9af#codeceptjs%E3%81%A8%E3%81%AF