Vue3のVue Test Utils で地獄をみた(Chart.js部門)

あまり気力がないのでかなり端折って書きます。

環境

  • vue@3.2.19
  • vuex@4.0.2
  • chart.js@2.9.4
  • @vue/test-utils@2.0.0-rc.15

ここで関係のなさそうな一般ライブラリが入っているってことは、つまりそういうことなんです。

問題の部分

本当ならもっと長いけど、問題となった部分はここだけ

src/components/Chart.vue

<template>
  <div class="chart">
    <canvas id="myChart"></canvas>
  </div>
</template>

<script>
import Chart from "chart.js";
import "chartjs-plugin-colorschemes";

export default {
  ...略
  methods: {
    drawLineChart() {
      const ctx = document.getElementById("myChart");

      // グラフの作成及び設定を指定する
      window.populationChart = new Chart(ctx, {
	    ...略
	  });
    },
  },
  mounted() {
    // グラフの作成
    this.drawLineChart();
  },
};
</script>

tests/unit/Chart.spec.js

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


test("Chart", () => {
  const state = {
	...}

  const $store = {
	...}

  const wrapper = shallowMount(Chart, {
    global: {
      mocks: {
        $store
      }
    }
  })
});

何が起きたのか・エラー内容

なんと!こんな素敵なエラーが出てきました✨✨

FAIL  tests/unit/PopulationChart.spec.js
 ● Console

console.warn node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:6465
  [Vue warn]: Unhandled error during execution of mounted hook 
	at <PopulationChart ref="VTU_COMPONENT" > 
	at <VTUROOT>

 ● 

  TypeError: Cannot read property 'length' of null
  
  ...略

な…何を言っているのかわからねーと思うが、おれも何をされたのかわからなかった…

どこのことを言っているの分からなかったし、時間をかけて調べても何にも出てこない、マジで

はじめはVuexのストアが問題で発生していると思い込んで、ひたすらモック作成に取り組んでいたけど一向にエラー文が変わらない絶望…

そんなこんなで、色々いじくりまわしてるとあることに気づいた

TypeError: Cannot read property 'length' of null

  at Object.acquireContext (node_modules/chart.js/dist/Chart.js:7756:19)
  at Chart.construct (node_modules/chart.js/dist/Chart.js:9324:26)
  at new Chart (node_modules/chart.js/dist/Chart.js:9311:7)
  at Proxy.drawLineChart (src/components/PopulationChart.vue:760:32)
  at Proxy.mounted (src/components/PopulationChart.vue:816:10)
  ...略

さっきは略していましたが、よく見るとChartやらContextやら書いてある…

もしかしてもしかするとこのChart.jsが問題のパターン?と思い、たくさん調べてみましたよ。

ライブラリの問題部分を読んでみると、どうやらコンストラクタの引数のcanvasエレメントが問題だった。さらに調べた結果、canvasが表示されきってないとかどうとかで読み込めなかったらしい。/(^o^)\ナンテコッタイ

問題の解決方法

ということで、数時間かけて問題に対処した結果、コードの修正部分は以下の通りです‼‼‼‼

src/components/Chart.vuemounted

  mounted() {
    // this.$nextTickで囲むだけ‼‼‼‼‼‼ 簡単だね‼
    this.$nextTick(function () {
      // グラフの作成
      this.drawLineChart();
    });
  },

そう、ここだけ‼

canvasがマウントされきっていないため、$nextTickを使ってすべてのコンポーネントがマウントされるのを待ったらいけました。

というのも、mountedでは子コンポーネント全てをマウントしたことは保証しないらしく、すべてのコンポーネントがマウントされているとは限らないそうです。公式ドキュメントにも普通に書いてあったので参考に載っけときます。

おわりに

数時間悩んだ結果2行追加で解決でつらいよ、俺の努力はいったい…

/(^o^)\ナンテコッタイ

深夜テンションで書きましたのでもう寝ます( ˘ω˘)スヤァ

誤字脱字はゆるしてください

参考

Vueのライフサイクルを完全に理解した - Qiita
API — Vue.js