Vue.jsでよく使うテストコード(jest/test-utils)

Vue/Vuexのテストコードをまとめてみました。
「jest」と「@vue/test-utils」を使用しています。

## 環境

- vue: ^2.6.10
- vuex: ^3.1.0
- jest: ^24.7.1
- @vue/test-utils: ^1.0.0-beta.29

## clickメソッドが実行されるかどうか

``` javascript:test.vue

button(
  @click="clickButton"
)

methods: {
  clickButton() {
    console.log('click button');
  },
},
```

``` javascript:test
describe('v-on', () => {
  test('clickでclickButtonメソッドが実行されること', () => {
    const wrapper = shallowMount(test);
    const stub = jest.fn();
    wrapper.setMethods({
      clickButton: stub,
    });
    wrapper.trigger('click');
    expect(stub).toHaveBeenCalled();
  });
});
```

## slotにコンテンツが挿入できてること

``` javascript:test.vue
div
  slot
```

``` javascript:test
describe('slot', () => {
  test('slotにコンテンツが挿入できること', () => {
    const wrapper = shallowMount(test, {
      slots: {
        default: '<div data-test="slotContent">slot content</div>',
      },
    });
    const slotContent = wrapper.find('[data-test="slotContent"]');
    expect(slotContent.exists()).toBe(true);
    expect(slotContent.text()).toBe('slot content');
  });
});
```

## propsが受け取れること

``` javascript:test.vue
props: {
  hoge: {
    type: String,
    default: '',
    required: true,
  },
},
```

``` javascript:test
describe('props', () => {
  test('propsを受け取れること', () => {
    const wrapper = shallowMount(test);
    wrapper.setProps({
      hoge: 'hogeだよ',
    });
    expect(wrapper.vm.$props.hoge).toBe('hogeだよ');
  });
});
```

## propsの型が合っているかどうか

``` javascript:test.vue
props: {
  hoge: {
    type: String,
    default: '',
    required: true,
  },
},
```

``` javascript:test.vue
describe('props', () => {
  test('型が期待しているとおりか', () => {
    const requiredProps = {
      hoge: 'hogeだよ',
    };
    const wrapper = shallowMount(test, {
      propsData: requiredProps,
    });
    expect(typeof wrapper.vm.$props.hoge).toBe('string');
  });
});
```

## propsがrequired:trueになっているかどうか

``` javascript:test.vue
props: {
  hoge: {
    type: String,
    default: '',
    required: true,
  },
},
```

``` javascript:test
describe('props', () => {
  test('requiredがtrueになっていること', () => {
    const requiredProps = {
      hoge: 'hogeだよ',
    };
    const wrapper = shallowMount(test, {
      propsData: requiredProps,
    });
    expect(wrapper.vm.$options.props.hoge.required).toBe(true);
  });
});
```

## emitされること

``` javascript:test.vue
input(
  type="text"
  @input="inputText"
)

methods: {
  inputText(e) {
    this.$emit('input-text', e);
  },
},
```

``` javascript:test
describe('inputText', () => {
    test('emitされること', () => {
      const wrapper = shallowMount(test);
      wrapper.vm.inputText({ target: { value: 'valueだよ' } });
      expect(wrapper.emitted('input-text')).not.toBeUndefined();
    });
  });
});
```

## emitで値が渡されること

``` javascript:test.vue
input(
  type="text"
  @input="inputText"
)

methods: {
  inputText(e) {
    this.$emit('input-text', e);
  },
},
```

``` javascript:test
describe('inputText', () => {
  test('emitで値が渡されること', () => {
    const wrapper = shallowMount(test);
    wrapper.vm.inputText({ target: { value: 'valueだよ' } });
    expect(wrapper.emitted('input-text')[0][0].target.value).toBe('valueだよ');
  });
});
```

## stateの値をpropsに渡せているかどうか(mapState使用)

``` javascript:Sample.vue
<template lang="pug">
  te-sample(
    :label01="label01"
  )
</template>

<script>
import { mapState } from 'vuex';
import TeSample from '@/components/templates/TeSample';

export default {
  components: {
    TeSample,
  },
  computed: {
    ...mapState({
      label01(state) {
        return state.label01;
      },
    }),
  },
};
</script>
```

``` javascript:test
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Sample from '@/components/pages/Sample';
import TeSample from '@/components/templates/TeSample';

const localVue = createLocalVue();
localVue.use(Vuex);

const store = new Vuex.Store({
  state: {
    label01: 'valueだよ',
  },
});

describe('Sample test', () => {
  let wrapper;
  beforeEach(() => {
    wrapper = shallowMount(Sample, {
      store,
      localVue,
    });
  });
  describe('mount', () => {
    test('TeSampleにstateの値をpropsで渡せているかどうか', () => {
      expect(wrapper.find(TeSample).props().label01).toBe('valueだよ');
    });
  });
});
```

## dispatchした値が渡されるかどうか

``` javascript:test.vue
<template lang="pug">
  div
    slot(
      :inputText="inputText"
    )
</template>

<script>
export default {
  props: {
    actionType: {
      type: String,
      default: '',
      required: true,
    },
  },
  methods: {
    inputText(e) {
      return this.$store.dispatch(this.actionType, {
        value: e.target.value,
      });
    },
  },
};
</script>
```

``` javascript:test
import Vuex from 'vuex';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import test from '@/components/test';

const localVue = createLocalVue();
localVue.use(Vuex);

describe('test', () => {
  let wrapper;
  const mockStore = {
    dispatch: jest.fn(),
  };
  beforeEach(() => {
    wrapper = shallowMount(test, {
      mocks: {
        $store: mockStore,
      },
      localVue,
    });
  });
  describe('mount', () => {
    test('dispatchで値が渡されること', () => {
      wrapper.setProps({
        actionType: 'actionだよ',
      });
      wrapper.vm.inputText({ target: { value: 'valueだよ' } });
      expect(mockStore.dispatch).toHaveBeenCalledWith(
        'actionだよ', {
          value: 'valueだよ',
        },
      );
    });
  });
});
```

## actionでcommitの値を渡せているかどうか

``` javascript:actions.js
export const SAMPLE_ACTION = 'SAMPLE_ACTION';

const actions = {
  action01(context, payload) {
    context.commit(SAMPLE_ACTION, payload);
  },
};

export default actions;
```


``` javascript:test
import actions, { SAMPLE_ACTION } from '@/store/actions';

describe('actions test', () => {
  test('action01のcommitでデータを渡せるかどうか', () => {
    const commit = jest.fn();
    const { action01 } = actions;
    const payload = {
      value: 'valueだよ',
    };
    action01({ commit }, payload);

    expect(commit).toHaveBeenCalledWith(SAMPLE_ACTION, payload);
  });
});
```

## mutationでStateの値が更新できてるかどうか

``` javascript:mutations.js
import { SAMPLE_ACTION } from './actions';

const mutations = {
  [SAMPLE_ACTION](state, inputData) {
    const data = state;
    const { value, name } = inputData;
    data[name] = value;
  },
};

export default mutations;
```

``` javascript:test
import mutations from '@/store/mutations';

describe('SAMPLE_ACTION test', () => {
  test('SAMPLE_ACTIONのactionでstateを更新する', () => {
    const inputData = {
      name: 'label01',
      value: 'valueだよ',
    };
    const state = {
      label01: '',
    };

    mutations.SAMPLE_ACTION(state, inputData);

    expect(state).toEqual({
      label01: 'valueだよ',
    });
  });
});
```