[QA Wolf] ブラウザ操作からテストコードを生成する？ 話題のテストライブラリ QA Wolfを使ってみる

先月くらいにTwitterで少し話題になっていた、ブラウザテストの自動生成ライブラリ QA Wolfを使ってみたので紹介します。


# QA Wolfとは？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114396/7f4cf910-1100-4219-112e-c07806bda62b.png)

https://github.com/qawolf/qawolf

ブラウザ上の操作から、Puppetterと、Jestを使ったブラウザテストを生成するライブラリです。
QA WolfのGitHubに掲載されている以下GIFのように簡単にテストコードを生成できます。
![68747470733a2f2f73746f726167652e676f6f676c65617069732e636f6d2f646f63732e7161776f6c662e636f6d2f686f6d652f6372656174652d746573742d736d616c6c2e676966.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114396/a8ca5ec6-59ac-1e50-d8d4-5b451ac1d7d2.gif)


* ボイラープレートを書く必要がない
* 同期操作と、スマートセレクタで安定的なテストを構築できる
* 複雑な操作のテストも手軽にコード化できる
* CIのセットアップも簡単に
* CIで自動的にIFや、MP4を生成してデバックも簡単

という特徴があります。

# テスト対象のVue.jsアプリ
今回はテスト用に作った簡易なVue.jsアプリをサンプルにQA Wolfを実行します。

リポジトリはこちら。
https://github.com/kawamataryo/sandbox-vue-qa-wolf

テストする機能はこちらです。

**fullnameの表示**
Homeページで、fistnameとlastnameを入力すると、それらを結合したfullnameを表示する

![Jan-29-2020 07-37-47.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114396/64709a84-9ae6-cbbc-ebcb-45c3c15c1044.gif)


これをQA Wolfでテストしていきます。

# QA Wolfでのテスト
## インストール
テスト対象のディレクトリでQA Wolfをインストールします。

```bash
yarn add qawolf
```

## ブラウザテストの作成
次にブラウザテストを作成します。

QA Wolfではブラウザでテストを実行するので、まずローカルサーバーでVueアプリを起動します。

```bash
yarn run serve --port 8088
```

次にfullnameの表示のテストを作成します。

```bash
npx qawolf create http://localhost:8088 fullmessage
```

すると、Chromiumのブラウザが起動するので、firstnameとlastnameに文字列を入力します。

![Jan-30-2020 16-32-20.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114396/bcb5629a-79a8-00a0-639a-8d6937bf6982.gif)


入力した後、コマンドを入力したシェルに戻り、`Y`と`Enter`を押します
以下のような表示がでれば完了です。

```
  ✔ Capturing browser actions for "fullmessage.test.js"
  ✔ Saving 
"/Users/kawamataryou/.ghq/github.com/kawamataryo/sandbox-vue-qa-wolf/.qawolf/tests/fullmessage.test.j
s"
```

2つのファイルが自動作成されているはずです。

```
.qawolf
├── tests
│   └── fullmessage.test.js
└── selectors
   └── fullmessage.json
```

中身を見ていきましょう。

まず、`selectors/fullmessage.json`

```
[
 {
  "index": 0,
  "css": "[data-test='firstname-input']"
 },
 {
  "index": 1,
  "css": "[data-test='firstname-input']"
 },
 {
  "index": 2,
  "css": "[data-test='firstname-input']"
 },
 {
  "index": 3,
  "css": "[data-test='lastname-input']"
 }
]
```

事前にVue.jsアプリ側でtemplateに埋め込んでいたdata-test="xxx"を認識して、セレクタが生成されています。
こちらをテストコードから参照できます。
（data-test="xxx"を入力しない場合は、操作単位のNodeを解析して自動的に親ノード、class、idより一意に決まるセレクタを生成してくれます。解説は、[こちら](https://docs.qawolf.com/docs/review_test_code#element-selectors)をご覧ください。）

次に`tests/fullmessage.test.js`

```fullmessage.test.js
const { launch } = require("qawolf");
const selectors = require("../selectors/fullmessage");

describe('fullmessage', () => {
  let browser;

  beforeAll(async () => {
    browser = await launch({ url: "http://localhost:8088/" });
  });

  afterAll(() => browser.close());

  it('can click "First name" input', async () => {
    await browser.click({ css: "[data-test='firstname-input']" });
  });

  it('can type into "First name" input', async () => {
    await browser.type({ css: "[data-test='firstname-input']" }, "kawamata");
  });

  it('can Tab', async () => {
    await browser.type({ css: "[data-test='firstname-input']" }, "↓Tab↑Tab");
  });

  it('can type into "Last name" input', async () => {
    await browser.type({ css: "[data-test='lastname-input']" }, "ryo");
  });
});
```

操作単位で、テストが書かれてしかもコメントまで自動で生成されていることがわかります。
すごい..。


## テストの修正、assertionの追加

自動生成のコードだと
* タブ移動などもコード化されていて余計（これが必要になることもあると思います）
* assertionがない
という問題があるので修正します。

以下のように修正しました。

これで`fullname-field`に値が入力されていることを検証できます。

```fullmessage.test.js
const { launch } = require("qawolf");
const selectors = require("../selectors/fullname");

describe('showFullname', () => {
  let browser;

  beforeAll(async () => {
    browser = await launch({ url: process.env.QAW_URL || "http://localhost:8084/" });
  });

  afterAll(() => browser.close());

  it('Show fullname', async () => {
    await browser.type({ css: "[data-test='firstname-input']" }, "kawamata");
    await browser.type({ css: "[data-test='lastname-input']" }, "ryo");

    const fullnameElement = await browser.find({css: "[data-test='fullname-field']"});
    const fullnameText = await fullnameElement.evaluate( n => n.innerText);

    expect(fullnameText).toBe("kawamata ryo");
  });
});

```

これでテストを実行して通ればfullnameのテストは完了です。

```
npx qawolf test fullname
```


## Circle CIでのCI環境構築
次はCircle CIの設定をします。

コマンド一つでconfig.ymlの生成ができます。

```
npx qawolf circleci
```

以下ファイルが生成されるはずです。

```
.circleci
└── config.yml
```

自動生成されたconfig.ymlに、Vue.js アプリの起動等を追記します。
最終的に以下のようにしました。

```config.yml
version: 2
jobs:
  build:
    docker:
      - image: circleci/node:12.14.0-browsers
    steps:
      - checkout

      - restore_cache:
          key: dependency-cache-{{ checksum "yarn.lock" }}

      - run:
          command: npm install

      - save_cache:
          key: dependency-cache-{{ checksum "yarn.lock" }}
          paths:
            - ./node_modules

      - run:
          name: Start test with QA Wolf
          command: |
            ## Start local server
            yarn serve & npx wait-on http://localhost:8080
            ## Start test
            npx qawolf test
          environment:
            QAW_ARTIFACT_PATH: /tmp/artifacts
            QAW_URL: http://localhost:8080

      - store_artifacts:
          path: /tmp/artifacts

```

ポイントは、環境変数で`QAW_URL`を指定して、portを固定している点です。
環境変数に`QAW_URL`が指定されている場合は、テストもそちらを参照するように修正しましょう。

```diff:fullname.test.js
  beforeAll(async () => {
-    browser = await launch({ url: "http://localhost:8088/" });
+    browser = await launch({ url: process.env.QAW_URL || "http://localhost:8088/" });
  });
```

これでCircle CIの連携をした上でリポジトリにcommit、pushすればテストが実行されるはずです。

実行が完了すると自動的にArtifactsにテスト実行結果のログ（GIF、mp4）が生成されます。
テストが失敗した際に、こちらから原因を探ることも容易です。

<img width="1489" alt="スクリーンショット 2020-01-30 5.23.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114396/5a808bbb-9ae8-0ec2-8d7d-f79d269526ac.png">

（実際に撮影されたGIF）
![video_1580165150629.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114396/824eaf0a-3e33-3a97-4275-d887de552197.gif)



# 終わりに
以上QA Wolfの紹介でした。
操作をイメージしながらテストコードを書くのではなく、操作そのものがテストコードになる。というのはとても新鮮な体験でした。
また、公式ドキュメントが非常にわかりやすいのも良いです。

本プロダクトで利用するには、まだまだ検証しなくてはならない点多々あるのですが、個人開発で使う分にはかなり良さそうです。
今後も活用していきたいです。