子コンポーネントからの通知をテストしてみた話

#はじめに
[前回](https://qiita.com/okauend/items/e923ba18b3e7837dc7c8)の記事でAtomコンポーネントのテスト自動化を行う事ができました。
次はAtomとかを組み合わせたMoleculesレイヤーのコンポーネントのテストやろ！
ということでやってみました。

##こんなのをテストしてみよう
####Vue.js
```vuejs:parent.vue
<template>
    <child @click = "emitted">
    </child>
</template>

<script>
import child from '@/child.vue'
export default {
    name:"parent",
    methods:{
        emitted(value){
            this.$emit('click',value)
        }
    }
}
</script>
```
```vuejs:child.vue
<template>
    <button @click = "onClick">
</template>

<script>
export default {
    name:"atom-button",
    methods:{
        onClick(){
            this.$emit('click')
        }
    }
}
</script>
```
今回はさっくりこういうファイルを対象にやってみましょう。
Moleculesといいながら、`parent.vue`には`child.vue`が一つ配置してあるだけですが…。
機能としては、ボタンをクリックしたら子から親へ、親から更に上位へ通知が連鎖されるようにしております。
ということで、テストのほうもさっくり書いていきましょう。

```javascript:parent.spec.js
import { shallowMount } from '@vue/test-utils'
import parent from '@/components/parent.vue'
import child from '@/components/child.vue'

describe('parent.vue',()=>{
    it('ボタンをクリックしたら親へイベントを通知しているか',()=>{
        const warrper = shallowMount(parent)
    })
})
```
ベースはこう書いておきます。
さて、今回のテスト内容ですが、
ユーザーの操作と同じような**ボタンをクリックしたら果たして親からイベントが通知されるのか**
をテストしてみたいと思います。

####どうテストする？
単純に親のMethodである`emitted`を呼び出す事で、**親へイベントを通知**は行う事ができます。
ただし、これではユーザーの操作と同じような流れのテストを行う事はできません。
単純に子であるボタンコンポーネントのクリックイベントを行う…のは親からは厳しいですし、
それだと**単一ファイルのテスト**ではなくなってしまいます。
ではどうするか、

```
warrper.vm.$emit()
```
を使い、子の通知を擬似的に作成する方法で試したいと思います。

```javascript:parent.spec.js
describe('parent.vue',()=>{
    it('ボタンをクリックしたら親へイベントを通知しているか',()=>{
        const warrper = shallowMount(parent)
        const TEXT = "Vue.js"
        warrper.find(child).vm.$emit('click',TEXT)
        expect(warrper.emitted().click[0][0]).toBe(TEXT)
    })
})
```

`$emit`の使い方は*Vue*の時と同じですね。
第一引数に親でキャッチするイベント名を、第二引数に送りたい値を書きます。
そして、`emitted`ですでに発生したイベントをキャッチし、`toBe`で値の検証を行っております。
`yarn test:unit`を打ち込み、テストを実行してみますと…
![9474a91c11e41187cdcce8e4635566e8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257615/9eb3923d-2877-9485-0c3f-674baab9b052.png)

**成功**しました！
##複数あった場合は？
では複数ボタンがあった場合は？
当然ながら、コンポーネントによってはボタンが複数存在し、それぞれ紐付いたMethodが違う場合もあります。

```vuejs:parent.vue
<template>
    <div>
        <child @click = "emitted1">
        </child>
        <child @click = "emitted2">
        </child>
        <child @click = "emitted3">
        </child>
        <child @click = "emitted4">
        </child>
        <child @click = "emitted5">
        </child>
    </div>

</template>

<script>
import child from '@/components/child.vue'
export default {
    name:"parent",
    components:{
        child
    },
    methods:{
        emitted1:function(value){
            return this.$emit('click',value)
        },
        emitted2:function(value){
            const string = value + "!"
            return this.$emit('click',string)
        },
        emitted3:function(value){
            const string = value + "!!"
            return this.$emit('click',string)
        },
        emitted4:function(value){
            const string = value + "!!!"
            return this.$emit('click',string)
        },
        emitted5:function(value){
            const string = value + "!!!!"
            return this.$emit('click',string)
        }
    }
}
</script>
```
先程の親コンポーネントを変更してみました。
これで先程のテストを少し改良し、実行をやってみましょう。

```javascript:parent.spec.js
describe('parent.vue',()=>{
    it('クリックしたら親へイベントを通知しているか',()=>{
        const warrper = shallowMount(parent)
        const TEXT = "Vue.js"
        warrper.find(child).vm.$emit('click',TEXT)
        expect(warrper.emitted().click[0][0]).toBe(TEXT)
    })
})
```
![b0c8c96f7f64d7d8a5462370bb56037e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257615/6ef85407-dd45-743c-a111-ec7b7f560316.png)

**成功**しました！

#####2個目のテストをやってみよう
では、2個目のボタンをクリックしてみましょう！
…
……
………
どうクリックするの？

当然ながら`find`はどの言語でもそうですが最初にヒットした要素が対象になるので、2個目を指定することはできません。
そこで、この**`findAll`**を使います！
ただし、このままでは使えません。
なぜなら、これはArrayで取得されるため、インデックスを指定しないといけないからです。
では、よくあるインデックスの指定方で取ってみましょう！

```javascript:parent.spec.js
describe('parent.vue',()=>{
    it('クリックしたら親へイベントを通知しているか',()=>{
        const warrper = shallowMount(parent)
        const TEXT = "Vue.js"
        const btn = warrper.findAll(child)[1]
        btn.vm.$emit('click',TEXT)
        expect(warrper.emitted().click[0][0]).toBe(TEXT)
    })
})
```
エラーが出ました。
![4f812e702f5263899d5ee3c08f8d8cb2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257615/07091fcc-b520-4187-8726-00fb28370d54.png)
**vmが存在しないのでプロパティを読めねぇよ！**と言われてます
つまり、`[1]`では配列から値を取れないということになります。
これは困りました、どう配列から取るのか…。
と、いうことで[**公式ガイド**](https://vue-test-utils.vuejs.org/ja/api/wrapper/#findall-selector)を見ましょう。
…
……
………
どうやら、`at(n)`でインデックスを指定し対象の値を取るようです。
早速やってみましょう。

#####変えてみた
```javascript:parent.spec.js
describe('parent.vue',()=>{
    it('クリックしたら親へイベントを通知しているか',()=>{
        const warrper = shallowMount(parent)
        const TEXT = "Vue.js"
        const btn = warrper.findAll(child).at(1)
        btn.vm.$emit('click',TEXT)
        expect(warrper.emitted().click[0][0]).toBe(TEXT)
    })
})
```
実行してみましょう！
![be91362441799e018f72194fb68e7697.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257615/8d9388e3-65c7-d9f0-a9f5-0a18e112fdf1.png)
エラーが出ていますね。
内容を見てみると`emitted`で通知された値と、比較したい値が違うといっています。
`Vue.js!`は`emitted2`のメソッドで実行される場合のみの文字列なため、2個目のボタンが押されていることになります。
つまり2個目のを指定することに成功しました！
やったー！

#おわり
子のイベントを疑似的に発生させたい場合は、

```
warrper.vm.$emit()
``` 
を使います。
2個以上同じコンポーネントがあり、それぞれテストしたい場合には、
`findAll`で当てはまるものをすべて取得し、`at(n)`でインデックスを指定する。

```
 warrper.findAll(hogehoge).at(n)
```
今回はこれらを知り、身につけることができました。