# 算出プロパティ

算出プロパティは単純なJavaScriptのオブジェクトなのでテストを書くのが簡単です。

算出プロパティのテストを書く2つの方法を紹介します。numbersの算出プロパティによって偶数か奇数のリストをレンダーする<NumberRenderer>コンポーネントを作ります。

# テストを書く

<NumberRenderer>Booleanevenプロップを受け取ります。eventrueの場合、2, 4, 6, 8を表示します。falseの場合、1, 3, 5, 7, 9を表示します。numbersのプロパティで算出します。

# 値をレンダーしてテストを書く

テストを書いてみましょう。

import { shallowMount } from "@vue/test-utils"
import NumberRenderer from "@/components/NumberRenderer.vue"

describe("NumberRenderer", () => {
  it("偶数をレンダー", () => {
    const wrapper = shallowMount(NumberRenderer, {
      propsData: {
        even: true
      }
    })

    expect(wrapper.text()).toBe("2, 4, 6, 8")
  })
})

実行する前に<NumberRenderer>を書いてみます。

<template>
  <div>
  </div>
</template>

<script>
export default {
  name: "NumberRenderer",

  props: {
    even: {
      type: Boolean,
      required: true
    }
  }
}
</script>

開発しましょう。yarn test:unitを実行して、エラーメッセージによって進みます。

● NumberRenderer › 偶数をレンダー

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

  Expected: "2, 4, 6, 8"
  Received: ""

numbersの算出プロパティを書いてみます。

computed: {
  numbers() {
    const evens = []

    for (let i = 1; i < 10; i++) {
      if (i % 2 === 0) {
        evens.push(i)
      }
    }

    return evens
  }
}

テンプレートを更新して新しく書いたプロパティを使います:

<template>
  <div>
    {{ numbers }}
  </div>
</template>

yarn test:unitを実行すると:

FAIL  tests/unit/NumberRenderer.spec.js
● NumberRenderer › 偶数をレンダー

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

  Expected: "2, 4, 6, 8"
  Received: "[
    2,
    4,
    6,
    8
  ]"

数は正しいですが、リストをいい感じにフォーマットしたいです。返す値を更新します:

return evens.join(", ")

yarn test:unitを実行したらパスです。

# callでテストを書く

even: falseの場合のテストを追加しましょう。今回はコンポーネントをレンダーせずに算出プロパティをテストする方法を紹介します。

先にテストを書きます:

it("奇数をレンダー", () => {
  const localThis = { even: false }

  expect(NumberRenderer.computed.numbers.call(localThis)).toBe("1, 3, 5, 7, 9")
})

コンポーネントをshallowMountでレンダーしてwrapper.text()の検証をする代わりに、callで別のthisを渡します。テストをパスしてから、callを使わないとどうなるかをみます。

上に書いてあるテストを実行する:

FAIL  tests/unit/NumberRenderer.spec.js
● NumberRenderer › 奇数をレンダー

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

  Expected: "1, 3, 5, 7, 9"
  Received: "2, 4, 6, 8"

numbersを更新します:

numbers() {
  const evens = []
  const odds = []

  for (let i = 1; i < 10; i++) {
    if (i % 2 === 0) {
      evens.push(i)
    } else {
      odds.push(i)
    }
  }

  return this.even === true ? evens.join(", ") : odds.join(", ")
}

偶数と奇数の場合はパスです。callを使わないとどうなりますか?使わないように更新します:

it("奇数をレンダー", () => {
  const localThis = { even: false }

  expect(NumberRenderer.computed.numbers()).toBe("1, 3, 5, 7, 9")
})

このままでテストを実行すると失敗します。

FAIL  tests/unit/NumberRenderer.spec.js
● NumberRenderer › 奇数をレンダー

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

  Expected: "1, 3, 5, 7, 9"
  Received: "2, 4, 6, 8"

vuepropsthisにバンドしてくれます。shallowMountなどでレンダーしないので、thisに何もバインドしません。numbersの中でconsole.log(this)をするとthisが見えます。thisnumbersプロパティがあるcomputedオブジェクトとなります。

{ numbers: [Function: numbers] }

それでcallを使うと別のthis値を渡せます。上のテストでevenプロパティがあるthisオブジェクトを渡しました。

# callshallowMount?

算出プロパティのテストを書くにはcallshallowMountが両方とも便利なテクニックです。callが特に便利な場合は:

  • mounted などの インスタンスライフサイクルフック の実行に時間がかかる場合、またライフサイクルフックを呼び出したくない場合です。コンポーネントをレンダーしないので、ライフサイクルフックも実行しません。
  • thisを細かく設定したい場合

もちろんコンポーネントが正しくレンダーするテストも必要です。テストしたいことに合わせてもっとも適切なテクニックを選んで、エッジケースをちゃんとテストします。