Skip to content

Nuxt3 × Vuetify3 × Vitest × Vue Testing Libraryで快適なテスト開発環境を作る

Posted on:2023年1月21日

執筆時点で、Nuxt3 がリリースされてから約 2 ヶ月程度が立ちました。

title

Nuxt: The Intuitive Web Framework

Build your next Vue.js application with confidence using Nuxt. An open source framework under MIT li...

今回はタイトルの通り、以下の技術スタックで環境構築していきます。

インストール

まずは Nuxt3 のインストールから始めます。

npx nuxi init nuxt3-app

続いて、Vuetify3 のインストールを行います。

npm i -D vuetify@next vite-plugin-vuetify sass

その他、テストで利用する Vitest や Vue Testing Library 関連もインストールしてしまいましょう!

npm install -D vitest @testing-library/vue happy-dom

サンプルコンポーネントの作成

今回は、Vuetify のコンポーネントを利用してテストを行うため、サンプルコンポーネントを作成します。

// components/Button.vue

<template>
  <v-app>
    <v-btn @click="onClick">
      {{ label }}
    </v-btn>
  </v-app>
</template>

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  props: {
    label: String,
  },
  name: "Sample",
  setup() {
    const onClick = () => {
      console.log("clicked");
    };
    return { onClick };
  },
});
</script>

nuxt.config.ts のセットアップ

nuxt.config.tsを作成し、以下のように記述します。

import { defineNuxtConfig } from "nuxt/config";

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  css: ["vuetify/lib/styles/main.sass"],
  build: {
    transpile: ["vuetify"],
  },
  vite: {
    define: {
      "process.env.DEBUG": false,
    },
  },
});

Vitest のセットアップ

vitest.config.tsを作成し、以下のように記述します。
Vitest で Vuetify を利用するためには、vite-plugin-vuetifyも必要になります。

// vitest.config.ts

/// <reference types="vitest" />
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vuetify from "vite-plugin-vuetify";
import { fileURLToPath, URL } from "node:url";

export default defineConfig({
  plugins: [
    {
      name: "vitest-plugin-beforeall",
      config: () => ({
        test: {
          setupFiles: [
            fileURLToPath(new URL("./vitest/beforeAll.ts", import.meta.url)),
          ],
        },
      }),
    },
    vue(),
    vuetify({
      autoImport: true,
      // styles: { configFile: './src/styles/variables.scss' },
    }),
  ],
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./", import.meta.url)),
      "~": fileURLToPath(new URL("./", import.meta.url)),
    },
  },

  test: {
    root: ".",
    globals: true,
    globalSetup: [fileURLToPath(new URL("./vitest/setup.ts", import.meta.url))],
    environment: "happy-dom",
    deps: {
      inline: ["vuetify"],
    },
  },
});

plugins/vuetify.tsも作成し、以下のように記述します。

// plugins/vuetify.ts

import { createVuetify } from "vuetify";
import * as components from "vuetify/components";
import * as directives from "vuetify/directives";

export default defineNuxtPlugin(nuxtApp => {
  const vuetify = createVuetify({
    components,
    directives,
  });

  nuxtApp.vueApp.use(vuetify);
});

続いて、/vitest/beforeAll.ts/vitest/setup.tsを作成します。
これがないと、css.supportsでエラーが発生するためです。

// /vitest/beforeAll.ts

import { beforeAll } from "vitest";
(global as any).CSS = { supports: () => false };

beforeAll(() => {
  global.CSS = {
    supports: (str: string) => false,
    escape: (str: string) => str,
  };
});
// /vitest/setup.ts

export async function setup() {
  global.CSS = {
    supports: (str: string) => false,
    escape: (str: string) => str,
  };
}

テストコードの作成

テストコードを実装していきますが、Testing Library に Vuetify をマウントする必要があるため、まずはrenderWithVuetifyというカスタムラッパーの関数を用意します。

// renderWithVuetify.ts

import { render } from "@testing-library/vue";
import { createVuetify } from "vuetify";

const vuetify = createVuetify({
  // vuetifyの設定を必要に応じて記述
});

const renderWithVuetify: typeof render = (component, options) => {
  const root = document.createElement("div");
  root.setAttribute("data-app", "true");

  return render(component, {
    container: document.body.appendChild(root),
    global: {
      plugins: [vuetify],
    },
    ...options,
  });
};

export default renderWithVuetify;

重要になるのはrender関数のオプションで用意しているglobal.pluginsキーで、ここに Vuetify を読み込ませる必要があります。

global: {
  plugins: [vuetify],
},

Vue2 の場合、以下 testing-library が用意していたコードサンプルを元に実装していましたが、Vue3 の場合は上記のようにglobal.pluginsキーを利用する必要があります。
自分が探す限り、どこにもそのようなサンプルコードがなかったので結構時間を取られてしまいました。。

title

vue-testing-library/vuetify.js at main · testing-library/vue-testing-library

🦎 Simple and complete Vue.js testing utilities that encourage good testing practices. - vue-testing...

そしてコンポーネントテスト用のファイルを作成します。

// components/Button.spec.ts

import { describe, it, expect } from "vitest";
import { fireEvent } from "@testing-library/vue";
import customRender from "@/customRender";
import Button from "./index.vue";

const props = {
  label: "Test Text",
};

describe("Button", () => {
  it("should render correctly", () => {
    const { getByRole } = customRender(Button, {
      props,
    });

    expect(getByRole("button")).toBeTruthy();
  });

  it("should emit click event", () => {
    const { getByText } = customRender(Button, {
      props,
    });

    fireEvent.click(getByText(props.label));
  });
});

テストの実行

最後に、package.jsonにテストコマンドを追加します。

{
  "scripts": {
    "test": "vitest"
  }
}

以下実行して、テストが通ることを確認します。

npm run test
 ✓ components/Button.spec.ts (2)

 Test Files  1 passed (1)
      Tests  2 passed (2)
   Start at  13:43:41
   Duration  3.21s (transform 1.76s, setup 62ms, collect 1.30s, tests 47ms)

無事テストが通りました!

今回作成した環境をリポジトリにアップしましたので、参考にしてみてください。

title

GitHub - hsmon/nuxt3-vuetify-vitest-testing-library

Contribute to hsmon/nuxt3-vuetify-vitest-testing-library development by creating an account on GitHu...

参考させていただいたリンク集

https://codybontecou.com/how-to-use-vuetify-with-nuxt-3.html
https://future-architect.github.io/articles/20210614b/
https://github.com/logue/vite-vuetify-ts-starter
https://qiita.com/MS-0610/items/a16670713de929a5b9b1