2020年 2月のJestセットアップ

ありとあらゆるリファレンスを読みまくってまとめました。
一番スッキリJestをセットアップしよう。

# Jestとは
Facebookが開発したJavaScriptの単体テストフレームワーク。
Node上で動作するため、手軽にテストを実行できる事が特徴。

[Jest · 🃏快適なJavaScriptのテスト](https://jestjs.io/ja/)

# 作業環境
- Laravel 5.7
- Docker on Mac
- Vue.jsとかはLaravel mixのもの

# セットアップ
## Jestとかを`npm install`

```shell
npm install --save-dev jest babel-jest @babel/core babel-core@bridge @babel/preset-env vue-jest @vue/test-utils
```

### それぞれのひとこと説明

**jest**
Jest本体

**babel-jest**
babelとjestを連携させるもの

**@babel/core**
Babel本体の最新版
Babelは八百万のJavaScript構文を一定のバージョンと互換のある構文に変換するもの。
Nodeでは最新のJavaScript構文（import / exportなど含め）が動作しません。
そのため、Babelを使ってトランスパイルしてあげます。

**babel-core@bridge**
`vue-jest`が`@babel/core`ではなく、`babel-core`を参照してしまうらしく、これを解決するためにインストール。
Babel 7.x系だけど名前は`babel-core`なBabel本体。

**@babel/preset-env**
山ほどあるBabelの構文変換に関わるライブラリをいい感じに自動的に使ってくれるもの。
参考 : [babel-preset-envを簡単にさわってみた。 - Qiita](https://qiita.com/ryuone/items/13f5d450c3865709ba10)

**vue-jest**
vueをjestでテストできるようにするもの

**@vue/test-utils**
Vueのテストを書きやすくしてくれるユーティリティ

### Babelのバージョンに注意
Babel 7.xからプラグインのプレフィックスとして`@babel/`がつくようになりました。
6.x以前のプラグインを使うとうまく動かない事があるらしい

## 設定ファイルをつくる
### babel.config.js (.babelrc)
Babelの設定ファイルです。
`babel.config.js`でも`.babelrc`でもいいのでソースルートに配置します。おそらく両方あるのはよろしく無いのでどちらか片方にしましょう。

```js:babel.config.js
module.exports = {
    presets: [
        ['@babel/preset-env', {modules: false}]
    ],
    env: {
        test: {
            presets: [
                ['@babel/preset-env', {targets: {node: 'current'}}]
            ]
        }
    }
};

```

### jest.config.js
どんなファイル名のものをテスト対象とするか、どのファイル名をどのモジュールで変換するか、などを設定できます。

```js:jest.config.js
module.exports = {
    transform: {
        '.*\\.(vue)$': '<rootDir>/node_modules/vue-jest',
        '^.+\\.js$': '<rootDir>/node_modules/babel-jest'
    },
    moduleFileExtensions: [
        'js',
        'vue'
    ],
};

```

### package.json (既存のものを編集)
Jestの起動コマンドを書き足します。
`NODE_ENV=test`とすることでNodeへテスト環境であることを明示しています。

```json:package.json
{
    "private": true,
    "scripts": {
        "dev": "npm run development",
        "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "watch": "npm run development -- --watch",
        ... ,
        "test": "NODE_ENV=test jest"  <-追記
    },
    "devDependencies": { ... },
    ...
}
```

## テストを書く
テストケースを作ってみましょう。

こんなファイルをテスト対象にしてみました。
カリー化してある2つの数字を足すだけの関数です。

```js:ExampleSum.js
export default{
    sum(x){
        return (y){
            return x + y;
        }
    }
}
```

これをテストするスクリプトはこんな感じ。

```js:example.test.js
import ExampleSum from "path/to/ExampleSum";

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

## 実行してみよう
`npm test`とターミナルへタイプしてJestを動かしてみよう。
`--`でファイル名を連結することでそのファイル名と部分一致するファイルのみをテスト対象としてくれます。

```shell
npm test -- example.test.js
```

そのほかのCLIオプションは [Jest CLI Options · Jest](https://jestjs.io/docs/ja/cli) へ。

### `async`/`await`もテストできる

```js:PromiseExample.js
export default {
    sleep() {
        return new Promise(resolve => {
            setTimeout(() => {
                resolve();
            }, 500);
        });
    }
}
```

```js
test('test async', async () => {
    await PromiseExemple.sleep();
    expect(true).toBe(true);
});
```

:smiley: :tada:

### Vueのフロントエンドテストもかけます
[ガイド | Vue Test Utils](https://vue-test-utils.vuejs.org/ja/guides/#%E3%81%AF%E3%81%98%E3%82%81%E3%82%8B)

```vue:ExampleComponent.vue
<template>

</template>

<script>
    export default {
        name: "ExampleComponent",
        methods: {
            sum(x, y) {
                return x + y;
            }
        }
    }
</script>

<style scoped>

</style>
```

```js:component.test.js
import ExampleComponent from "./models/ExampleComponent";

const {mount} = require("@vue/test-utils");

test('Vue component test', () => {
    const component = mount(ExampleComponent);
    expect(component.isVueInstance()).toBeTruthy();
    expect(component.vm.sum(1, 2)).toBe(3);
});
```

`component.vm`が僕らのよく知るVueコンポーネントインスタンスです。
DOMを操作してフォーム入力をしたり、クリックしたりもできます。

---

`@babel/preset-env`がびっくりするほど便利だったことがわかった。