Mac + Vue + Vue Test Utils + Vuetify の開発環境構築


#### nodebrew インストール

```
$ brew install nodebrew
$ nodebrew help|head -n1
nodebrew 1.0.1

$ nodebrew setup
Fetching nodebrew...
Installed nodebrew in $HOME/.nodebrew

========================================
Export a path to nodebrew:

export PATH=$HOME/.nodebrew/current/bin:$PATH
========================================

$ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile
$ source ~/.bash_profile
$ nodebrew install-binary stable
$ nodebrew use stable
$ node -v
v12.4.0

$ nodebrew use v10.16.0
$ node -v
v10.16.0

$ npm update -g npm
$ npm -v
6.14.3
```

#### yarn インストール
```
$ brew install yarn
$ yarn --version
1.17.0
```

#### @vue/cli インストール

```
$ yarn global add @vue/cli
$ yarn global upgrade --latest @vue/cli
@vue/cli 4.2.3
$ yarn info vue | grep version:
  version: '2.6.11',
```


#### Vue インストール

```
$ mkdir -p ~/Developer/sample-vue && cd ~/Developer/sample-vue
$ vue create sample-app
$ yarn serve
```

↓ http://127.0.0.1:8080/

![vue.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24811/697fbc2e-2566-54f4-7e59-e54f4628e8e6.png)

##### Vue インストール後のツリー

```
~/Developer/sample-vue/sample-app
├── README.md
├── babel.config.js
├── node_modules
├── package.json
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── App.vue
│   ├── assets
│   │   └── logo.png
│   ├── components
│   │   └── HelloWorld.vue
│   └── main.js
└── yarn.lock
$ yarn serve
```


#### Vue Test Utils インストール
```
$ cd ~/Developer/sample-vue/sample-app
$ vue add --dev  @vue/test-utils @vue/cli-plugin-unit-jest
```

##### Vue Test Utils インストール後のツリー
```
~/Developer/sample-vue/sample-app
├── README.md
├── babel.config.js
├── jest.config.js ■ 追加
├── node_modules
├── package.json
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── App.vue
│   ├── assets
│   │   └── logo.png
│   ├── components
│   │   └── HelloWorld.vue
│   └── main.js
├── tests ■ 追加
│   └── unit ■ 追加
│       └── example.spec.js ■ 追加
└── yarn.lock
```

##### TEST 実行結果

```
$ cd ~/Developer/sample-vue/sample-app
$ yarn test:unit tests/unit/example.spec.js
yarn run v1.22.4
$ vue-cli-service test:unit tests/unit/example.spec.js
 PASS  tests/unit/example.spec.js
  HelloWorld.vue
    ✓ renders props.msg when passed (26ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.172s
Ran all test suites matching /tests\/unit\/example.spec.js/i.
✨  Done in 3.99s.
```

#### Vuetify インストール

```
$ cd ~/Developer/sample-vue/sample-app
$ vue add vuetify
$ yarn info vuetify | grep version:
  version: '2.2.18',
$ yarn serve
```
↓ http://127.0.0.1:8080/

![vuetify.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24811/f3e521bf-2004-3557-4b58-704ccd2fd996.png)


```
~/Developer/sample-vue/sample-app
├── README.md
├── babel.config.js
├── jest.config.js
├── node_modules
├── package.json
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── App.vue
│   ├── assets
│   │   ├── logo.png
│   │   └── logo.svg ■ 追加
│   ├── components
│   │   └── HelloWorld.vue
│   ├── main.js
│   └── plugins ■ 追加
│       └── vuetify.js ■ 追加
├── tests
│   └── unit
│       └── example.spec.js
├── vue.config.js
└── yarn.lock
```



#### TEST

```
vi ~/Developer/sample-vue/sample-app/src/components/HelloWorld.vue
```

```~/Developer/sample-vue/sample-app/src/components/HelloWorld.vue
<template>
  <div>
    <p id="htmlMessage">{{ message }}</p>
    <v-btn @click="onClick" small>CountUp</v-btn>
    <p id="count">{{ count }}</p>
  </div>
</template>

<script>
  export default {
    data: () => ({
      message: 'HelloWorld',
      count: 0
    }),
    methods: {
      onClick () {
        this.count++
      }
    }
  }
</script>
```

```
~/Developer/sample-vue/sample-app/tests/unit/example.spec.js
```


```~/Developer/sample-vue/sample-app/tests/unit/example.spec.js
import Vue from 'vue'
import Vuetify from 'vuetify'
import { createLocalVue, mount, shallowMount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'

Vue.use(Vuetify)

const localVue = createLocalVue()

describe('CustomCard.vue', () => {
  let vuetify
  let wrapper
  beforeEach(() => {
    vuetify = new Vuetify()
    wrapper = mount(HelloWorld, {
      localVue,
      vuetify,
    })
  })

  it('renders a vue instance', () => {
    expect(wrapper.isVueInstance()).toBe(true);
  })

  it('Checks the message of vm', () => {
    let vmMessage = wrapper.vm.message
    expect(vmMessage).toMatch('HelloWorld')
  })

  it('Checks the message of html', () => {
    let htmlMessage = wrapper.find('#htmlMessage').text()
    console.log(htmlMessage)
    expect(htmlMessage).toMatch('HelloWorld')
  })

})
```


#### ユニットテスト
```
$ cd ~/Developer/sample-vue/sample-app
$ yarn test:unit tests/unit/example.spec.js
yarn run v1.22.4
$ vue-cli-service test:unit tests/unit/example.spec.js
 PASS  tests/unit/example.spec.js
  CustomCard.vue
    ✓ renders a vue instance (81ms)
    ✓ Checks the message of vm (27ms)
    ✓ Checks the message of html (31ms)

  console.log tests/unit/example.spec.js:32
    HelloWorld

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        1.531s, estimated 2s
Ran all test suites matching /tests\/unit\/example.spec.js/i.
✨  Done in 2.79s.
```

#### Vueでunitテストをする為の基礎基礎メモ
https://www.nogson.blog/entry/2019/01/30/175723

| 関数名 | 説明 | 利用ケース |
|--- |--- |--- |
| mount | 子コンポーネントに実際のコンポーネントを使う | コンポーネント同士で連携が必要な場合 |
| shallowMount | 子コンポーネントにダミーのコンポーネントを使う | コンポーネント単体でのテスト |

| Matcher | 説明 | 使用例 |
|---|---|---|
| .toBe(value) | resultとactual等価であることの比較 | expect(4).toBe(4) |
| .toEqual(value) | resultとactualの連想配列の中身が一致するかの比較 | expect({two: 2,one: 1}).toEqual({one: 1,two: 2}) |
| .toMatch(regexp) | resultに対して正規表現で比較 | expect('abcdefg').toMatch(/abc/) |
| .toBeGreaterThan(number) | resultがactualより大きい | expect(11).toBeGreaterThan(10) |
| .toBeGreaterThanOrEqual(number) | resultがactual以上 | expect(10).toBeGreaterThanOrEqual(10) |
| .toBeLessThan(number) | resultがactualより小さい | expect(9).toBeLessThan(10) |
| .toBeLessThanOrEqual(number) | resultがactual以下 | expect(10).toBeLessThanOrEqual(10) |
| .toBeCloseTo(number,桁数) | resultの特定の少数桁までactualと一致するかを比較(jsは小数点計算で誤差がでる為) | expect(0.1 + 0.2).toBeCloseTo(0.35, 1) |
| .toHaveLength(number) | resultのlengthとactualと一致するかを比較 | expect([1,2]).toHaveLength(2) |
| .toBeInstanceOf(class) | resultがactualと同じclassのインスタンス化か比較 | class Klass {} |
| expect(new Klass()).toBeInstanceOf(Klass) |
| .toHaveProperty(value) | resultの特定のkeyと一致するか | expect({a:false,b:{c:1,d:'A'}}).toHaveProperty('a') // key aがあるか |
| expect({a:false,b:{c:1,d:'A'}}).toHaveProperty('a',false) // key aはfalseか |
| expect({a:false,b:{c:1,d:'A'}}).toHaveProperty('b.c',1) |
| .toMatchObject(value) | resultの一部とactualが一致するか | expect({a:1,b:2}).toMatchObject({a: 1}) |
| .toContain(value) | resultにactualが入っているか | expect([1,2]).toContain(2) |
| expect('ABCDEFG').toContain('EFG') |
| .toBeNull() | resultがNullかどうか | expect(null).toBeNull() |
| .toBeUndefined() | resultがUndefinedかどうか | expect().toBeUndefined() |

## 参考

インストール — Vue.js
https://jp.vuejs.org/v2/guide/installation.html

クイックスタート — Vuetify.js
https://vuetifyjs.com/ja/getting-started/quick-start/

Guides | Vue Test Utils
https://vue-test-utils.vuejs.org/guides/#getting-started

@vue/cli-plugin-unit-jest
https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-unit-jest

Expect · Jest
https://jestjs.io/docs/en/expect

ユニットテスト — Vuetify.js
https://vuetifyjs.com/ja/getting-started/unit-testing/