CircleCIを使ってテストを自動化する

## はじめに

> この記事は、[North Detail Advent Calendar 2019](https://qiita.com/advent-calendar/2019/northdetail) の10日目の記事です。

業務でテスト自動化が必要になったので調べてみたところ、
導入のハードルが思ったより低かったので備忘録として手順を書き残します:writing_hand:

今回は `GitHub` と `CircleCI` を組み合わせてテストを自動化した環境を作ります。

## CircleCI ？

> 性能、柔軟性、制御性を兼ね備えたプラットフォーム
> https://circleci.com/ja/product/

CIツールの1つです:recycle:
実行OSやジョブの並列実行数など、機能に制限はありますが無料で利用することができます。

Freeプランで注意すべきはクレジット数でしょうか。
2,500クレジット/週の使用制限がかかっており、ジョブを実行することで消費されていきます。

クレジットの消費数はマシンによって異なりますが、
例えば `Linux(Medium)` のマシンを使用する場合は
ビルド時間1分あたり10クレジット消費となっているため、250分/週に制限されます。

## 準備

### npm初期化 ~ Jestをインストール

自動化の前に、まずはテスト環境を構築します。
(ここでは`Jest`を使います)

```
$ npm init -y
```

質問をすっ飛ばして初期状態で作成します。

```
$ npm install jest
```

Jestをインストールし、package.jsonの `"scripts.test"` を以下のように変更します。

```json:package.json
"scripts": {
  "test": "jest"
}
```

これで `$ npm run test` を叩いた際にJestが呼ばれるようになりました。

### テスト対象のコードを作成

足し算するだけのjsファイルを作成してみます。

```javascript:calc.js
function sum(x, y) {
  return x + y;
}

module.exports.sum = sum;
```

### テストコードを作成

先ほど作成した足し算にテストコードを書きます。

```javascript:calc.test.js
const calc = require('./calc.js');

test('Sum test', () => {
  expect(calc.sum(1, 2)).toBe(3);
});
```

### テストを実行してみる

```
$ npm run test

 PASS  ./calc.test.js
  ✓ Sum test (4ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.527s
Ran all test suites.
```

無事に正常終了しました :ok_woman:

### 設定ファイルを作成

CircleCI用の設定ファイルを追加します。
今回は以下のような内容にしてみました。

1. 該当ブランチをcheckout
2. 必要なモジュールをインストール
3. テスト実行

```yml:.circleci/config.yml
# CircleCIのバージョン
version: 2
jobs:
  build:
    # 使用するDockerイメージ
    docker:
      - image: circleci/node:8.12.0
    working_directory: ~/repo
    # 処理内容
    steps:
      - checkout
      - run:
          name: Setting npm
          command: npm install
      - run:
          name: Run test
          command: npm run test
```

### GitHubへpush

画像のようなディレクトリ構成になっていればOK :ok_hand:
GitHubへpushして準備完了です。
<img width="280" alt="1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302049/d8b5203c-0a53-85af-dd15-6ce9fd302569.png">

## いざCircleCI

### CircleCIでプロジェクトを新規作成

先ほどpushしたリポジトリにアクセスできるユーザーでCircleCIへログインし、
サイドメニューから `Add Projects` をクリック。

リポジトリ一覧が表示されるので、処理したいリポジトリで「Set Up Project」をクリックします。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302049/375e136e-fd2f-bd39-decc-aa1bb85edeae.png)

新規CircleCIプロジェクトの設定画面に遷移しました。

今回は以下のような設定にします。

* Operating Systemを `Linux`
* Languageを `Node`

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302049/aae7ee7c-84c0-a400-3a14-ec37fccc864c.png)
設定ファイル(config.yml)の追加を求められますが、  
すでに作成済みなので「Start building」をクリックしてビルドを開始します。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302049/88420866-f736-aba9-ae48-8615d8939ca8.png)

設定に問題が無ければ実行結果の画面に遷移します。
ローカルと同じ結果になっていますね。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302049/23238af9-b253-7394-738c-5c644cca8423.png)
これでCircleCIの設定は完了です。
今後このリポジトリに対して変更が入るたびに実行されます。 

### テストを失敗させてみる

今回はテストを正常終了させましたが、
失敗していた場合はどうなるでしょうか？

派生ブランチを作成し、テストコードを以下のように変更してみます。

```javascript:calc.test.js
const calc = require('./calc.js');

test('Sum test', () => {
  expect(calc.sum(1,2)).toBe(300); // ここを変更
});
```

実行してみると、もちろん失敗します。

```
$ npm run test

 FAIL  ./calc.test.js
  ✕ Sum test (7ms)

  ● Sum test

    expect(received).toBe(expected) // Object.is equality

    Expected: 300
    Received: 3

      2 | 
      3 | test('Sum test', () => {
    > 4 |   expect(calc.sum(1,2)).toBe(300);
        |                         ^
      5 | });

      at Object.<anonymous> (calc.test.js:4:25)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        2.648s
Ran all test suites.
```

この状態でプルリクを投げてみましょう。
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302049/23cc2fb1-2d5f-cb28-cf83-d2d4879e889b.png)
`CircleCI` と連携したことで `GitHub` のプルリク画面にジョブの実行結果が表示されています。
テストで失敗しているため、警告が表示されて「Merge pull request」ボタンが非活性(のような色)になっています。

`Details` をクリックしてエラー内容を確認してみましょう。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302049/1fb683e0-5262-2f62-4926-9498b94a0415.png)
ローカルでの実行結果と同様にエラーが発生していました :ok_woman:

## まとめ

`GitHub` と `CircleCI` を組み合わせて、テストが自動化された環境を作りました。

自動化とか難しそう…と勝手なイメージから避けていたのですが、
実際にやってみると初心者でも簡単に導入できました :relaxed:

ユニットテストの他にも自動化できると便利そうなものはありますが、
実行時間が長くなると消費クレジットも増えていきます。
詰め込みすぎには注意です :warning:

## 明日は

「North Detail Advent Calendar」 11日目は @ooishi-hy さんです！ :clap:
→ [僕の個人プロジェクトを加速させたNuxt](https://qiita.com/ooishi-hy/items/e42ff53e6c1e546a46ba)

## 参考リンク

* CircleCI
    * トップページ  
      https://circleci.com/
　
    * ドキュメント  
      https://circleci.com/docs/ja/
　
    * 料金表  
      https://circleci.com/ja/pricing/

* Jestのドキュメント
 https://jestjs.io/docs/ja/getting-started