TestCafeを使ってCircleCI上でE2Eテスト

CircleCI２でDockerな感じでやります。Circle2を初めて触ってて勉強成分が多いので色々間違ってるかもしれないです。

## やること

とりあえずこんだけ。

1. ci中でdockerで適当にWebAppを建てる
2. [TestCafe](https://github.com/DevExpress/testcafe)でテストする

順番を保証するためにdocker-composeを使って、WebAppが建ってからテストが流れるようにします。

## ディレクトリ構造

```bash
.
├── .circleci
│   └── config.yml # ci設定
├── .gitignore
├── docker-compose.ci.yml    # ci用
├── docker-compose.test.yml  # ローカルテスト用
├── docker-compose.yml       # 開発用
├── koa
│   ├── .dockerignore
│   ├── Dockerfile
│   ├── index.js
│   ├── node_modules
│   └── package.json
└── testcafe
    ├── .dockerignore
    ├── Dockerfile
    ├── node_modules
    ├── package.json
    ├── screenshots    # スクショ入れ
    └── tests          # テストを処理する.jsを入れる
``` 

何故か３つも`docker-compose*.yml`あるんですが、何故かci上だと`volumes`が効いてくれなくて空ディレクトリになっちゃう（調べてもﾜｶﾗﾝｶｯﾀ）問題が僕の環境だとなっちゃうので、上から

- Dockerfileの中でコピーだけ
- `volumes`でテストファイルをコンテナにマウント
- WebAppだけの記述

みたいにして分けてます... 

> ----
> ##### 追記 (2017-8-8)
>
> [CircleCI 2.0 で docker-compose を動かすなら、Machine Executor にしないとハマる](http://qiita.com/kunit/items/85e869b9a0ff7a30ac79)
> こちらの記事を参考にさせていただいて、DockerImageなマシンを使わずに、MachineExecutorなマシンを使うとちゃんと`volumes`でマウントさせることができました。
>
> ---

#
> 確認に使ったリポジトリ
> [https://github.com/nju33/example-e2e-using-testcafe-docker-image](https://github.com/nju33/example-e2e-using-testcafe-docker-image)

## WebAppを適当に作る

なんでもいいですが、今回は[Koa](https://github.com/koajs/koa)で作ります。

```bash
yarn add koa
```

適当に書いて、

```js:koaindex.js
const Koa = require('koa');
const app = new Koa();

app.use(ctx => {
  ctx.body = '<h1>ハロー</h1>';
});
app.listen(3000);
console.log('listen to 3000');
```

まるっと`koa/`をコピーして、デフォルトで`yarn serve`されるようしてるだけですね。

```docker:koa/Dockerfile
FROM node
MAINTAINER nju33<nju33.ki@gamil.com>

COPY . /var/src/app/
WORKDIR /var/src/app
RUN yarn
EXPOSE 3000
CMD ["yarn" , "serve"]
```

これで起動したら`localhost:3000`にアクセスできるようにすれば、「ハロー」と表示されます。WebApp終わり。

## TestCafe

TestCafeをインストールしときます。

```bash
yarn add -D testcafe
```

テストファイルは帰ってきた要素のテキストが「ハローか？」をチェックする簡単なのを用意。

```js:testcafe/tests/test.js
import {Selector} from 'testcafe';

fixture`HTMLテスト`
  .page`http://koa:3000`;

test('<h1/>のテキストはハロー', async t => {
  const h1 = Selector('h1');
  await t.expect(h1.innerText).eql('ハロー');
});
```

TestCafeは、[testcafe/testcafe](https://hub.docker.com/r/testcafe/testcafe/)というDockerイメージがあるので、これを使います。環境が整っていて、ローカルで`testcafe <args>`としていたのを`docker run testcafe/testcafe <args>`と置き換えればいいイメージです。

こちらも`testcafe/`をまるっとコピーしてテストとして実行するファイルとか配置してます。

```docker:testcafe/Dockerfile
FROM testcafe/testcafe
MAINTAINER nju33<nju33.ki@gmail.com>

USER root
COPY . /var/src/test
WORKDIR /var/src/test
RUN set -x \
  && apk update \
  && apk add --no-cache yarn \
  && yarn
```

## 組み立て

```yaml:docker-compose.ci.yml
version: '3'
services:
  koa:
    build:
      context: ./koa
  testcafe:
    depends_on:
      - koa
    build:
      context: ./testcafe
    command: >
      "chromium:headless --no-sandbox"
      /var/src/test/tests/*
```

`depends_on`で`koa`サービスの後に、`testcafe`サービスが走るようになってます。実行するには次のコマンド。

```bash
docker-compose -f docker-compose.ci.yml run --rm testcafe
```

## ci用の設定ファイル

v2から見るファイルが変って、`.circleci/config.yml`(`circle.yml`じゃない！)になりました。

```yaml:.circleci/config.yml
version: "2"
jobs:
  build:
    docker:
      - image: circleci/node:8.1.2
    working_directory: "~/test"
    steps:
      - checkout
      - setup_remote_docker
      - run: docker-compose -f docker-compose.ci.yml build
      - run: docker-compose -f docker-compose.ci.yml run testcafe
```

リポジトリに`push`されると、`~/test/`の中にプロジェクトを展開して、`koa`と`testcafe`サービスをビルドして、テストを走らせる感じのことをやってます。


`setup_remote_docker`はci中で`docker`と`docker-compose`を使う為の魔法のワードらしいです。

ちなみに、ビルドしたDockerイメージを`restore_cache`、`save_cache`するのもやってみたんですが、規模が小さいと逆に時間掛かるみたいなんでやってません。（勘違いかもしれない）

## 結果

という訳でこんな感じで通りましたぜ

![CircleCI_🔊.png](https://qiita-image-store.s3.amazonaws.com/0/84144/1dc7f5f4-0bb7-daf8-873e-bdd82ac4cd72.png)

---

`volumes`が効かないのはなんとかしたい…