【Google Maps API × Vitest】VitestでGoogle Maps APIを用いた処理のテストコードを書こう

この記事について

サービス開発統括本部 ソリューション開発部で農業プロダクトの開発を担当している西村です。

弊社でピンポイントタイム散布(以下、PTS)という防除のデジタル化サービスを展開しております。

PTSでは農地のデータを管理するため、Google Maps APIの活用は欠かせません。そのコアな技術を活用した処理は品質を担保する必要があります。

また、PTSはVue3系で開発しており、ビルドツールにはViteを使用しております。ViteはVue3系推奨されています。

そこで今回は、Viteを利用したテストフレームワーク、Vitestを使ったGoogle Maps APIのテストコードについて紹介します。

先に結論を3つ

  • VitestとはBlazing Fast Unit Test Frameworkとドキュメントに記載されている通り、とても処理が早いユニットテストフレームワークのこと
  • @googlemaps/jest-mocksは使えないので、@soleo/google-maps-vitest-mocksを使ってテストを実行
  • Jest版と同様に書くことが可能

Vitest とは

VitestとはBlazing Fast Unit Test Frameworkとドキュメントに記載されている通り、とんでもなく早いユニットテストフレームワークのことです。

説明は「Viteを利用したテストツールVitestの利用を始める」という記事に委ねます。わかりやすくまとめられていると思ったためです。VitestはJestと同じような記法で記載が可能です。

@soleo/google-maps-vitest-mocksとは

@soleo/google-maps-vitest-mocksはVitestにおいてGoogle Mapをモックしてくれるライブラリです。

Googleが公式に@googlemaps/jest-mocksというライブラリを提供しています。

@googlemaps/jest-mocksを使ってテストを実行してみると、以下の結果の通り実行に失敗します。

❯❯❯ npx vitest run -t "編集可能かどうかを判定するメソッドの戻り値がtrueであること"

 RUN  v1.6.0 /test/models

 ❯ CustomPolyline.test.ts (0)

⎯⎯⎯⎯⎯⎯⎯⎯ Failed Suites 1 ⎯⎯⎯⎯⎯⎯⎯⎯
 FAIL  CustomPolyline.test.ts [ CustomPolyline.test.ts ]
ReferenceError: jest is not defined
 ❯ Object.<anonymous> ../../../node_modules/@googlemaps/jest-mocks/src/import-library.ts:1:30

⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯⎯⎯⎯⎯⎯⎯⎯

 Test Files  1 failed (1)
      Tests  no tests
   Start at  21:09:11
   Duration  452ms (transform 63ms, setup 0ms, collect 0ms, tests 0ms, environment 101ms, prepare 55ms)

@soleo/google-maps-vitest-mocksを使ってテストを書き実行すると、以下の結果が得られます

❯❯❯ npx vitest run -t "編集可能かどうかを判定するメソッドの戻り値がtrueであること"

 RUN  v1.6.0 /test/models

 ✓ CustomPolyline.test.ts (4)
   ✓ CustomPolylineを編集可能かどうかを判定するメソッドであるisEditableのテスト

(省略)

 Test Files  1 passed (1)
      Tests  3 passed | 1 skipped (4)
   Start at  21:10:50
   Duration  445ms (transform 61ms, setup 0ms, collect 66ms, tests 3ms, environment 105ms, prepare 50ms)

実際に試してみましょう

今回もcustomizePolylineというクラスのテストを実行したいと仮定します。isEditableというメソッドが意図した値を返してくれるかというテストをしたいと思います。

Polyline とは

Google map上に書く線のことです。 (赤い矢印で示しているもの)

カスタマイズによっては、矢印のように書いたり、色や太さを変更させることができます。

以下で遊ぶことができます。

準備

仕様は以下の通りとします。 - 自分の作成したポリラインは編集可能 - 自分以外が作成したポリラインが編集不可 - 管理者の場合は自分、自分以外関係なく編集可能

これらの仕様を満たすクラスとメソッドを定義します。

// CustomPolyline.ts
export class CustomPolyline {
    id: string;
    polyline: google.maps.Polyline;
    colorCode: string;
    isOwnPolyline: boolean;
    createdId: number;

    constructor(
        id: string,
        polyline: google.maps.Polyline,
        colorCode: string,
        createdId: number,
        operatorUserId: number
    ) {
        this.id = id;
        this.polyline = polyline;
        this.colorCode = colorCode;
        this.isOwnPolyline = operatorUserId === createdId;
        this.createdId = createdId;
    }

    isEditable(hasAdminRole: boolean): boolean {
        if (hasAdminRole) {
            return true;
        } else {
            return this.isOwnPolyline;
        }
    }
}

テストを書いてみる

// CustomPolyline.test.ts

import { it, describe, expect } from "vitest";
import { useGoogleMapSetUp } from "./config/map";
import { CustomPolyline } from "./shared/models/CustomPolyline";
import { initialize, mockInstances, Polyline } from "@soleo/google-maps-vitest-mocks";


const { mapCommonOption } = useGoogleMapSetUp()

const initialPolyline = () => {
    const map = new google.maps.Map(document.createElement('div'));
    const polyline = new google.maps.Polyline({
      path: [
        new google.maps.LatLng({ lat: 34.40882247741932, lng: 136.6821998287318 }),
        new google.maps.LatLng({ lat: 34.40882247741933, lng: 136.6821998287319 }),
      ],
      ...mapCommonOption,
    });
    polyline.setMap(map);
  };

describe('CustomPolylineを編集可能かどうかを判定するメソッドであるisEditableのテスト', () => {
    initialize();
    initialPolyline();

    const mockPolylineList = mockInstances.get(Polyline);

    describe('編集者と作成者が同じの場合', () => {
        const hasAdminRole = false;

        const customizePolyline = new CustomPolyline(
            '01HY05BFHVNKRT3KSEP440Q845',
            mockPolylineList[0],
            '#ffffff',
            1,
            1
        );

        it('編集可能かどうかを判定するメソッドの戻り値がtrueであること', () => {
            expect(customizePolyline.isEditable(hasAdminRole)).toBe(true);
        })
    }),

    describe('編集者と作成者が同じでない場合', () => {
        // 省略
    }),

    describe('操作者が管理者の場合', () => {
        // 省略
    })
})

実行結果

❯❯❯ yarn test
yarn run v1.22.22
$ vitest

 DEV  v1.6.0 /vitest-try

 ✓ src/shared/models/CustomPolyline.ts (4)
 ✓ src/test/models/CustomPolyline.test.ts (4)

 Test Files  3 passed (3)
      Tests  9 passed (9)
   Start at  01:26:20
   Duration  507ms (transform 203ms, setup 0ms, collect 309ms, tests 22ms, environment 330ms, prepare 187ms)


 PASS  Waiting for file changes...
       press h to show help, press q to quit

もしmockがないとテストを実行した場合はgoogle is not definedが返ってきます。 そのため、@soleo/google-maps-vitest-mocksでモックを作成してテストを実行しましょう。

In-Source Testing

VitestではIn-source Testingを実行することができます。In-source Testingとは、実装コードと同じソースファイルにテストコードを記載し、そちらを実行できるというものです。Rustで有名なアプローチのようです。 公式ドキュメントに記載されているように、tsconfig.json, vite.config.tsに1、2行追加するだけで実施できます。

メリット

Vitest テストコードを実装ファイルと同一のファイルに記述する」の記載を引用します

  • private にしたい目的で export したくない関数をテストできる
  • 実装とテストの距離が近いのでテストが書きやすい(私はテストコードを書くときだけいつもエディタの画面を分割して表示してます)
  • さっとプロトタイプのコードを書くたいときに素早く書ける

お試し

実際にクラスやメソッドを定義しているファイルの中にテストコードを書いていきます。

// CustomPolyline.ts

import { useGoogleMapSetUp } from "./config/map";

// 実際にモデルやメソッドを定義する部分
export class CustomPolyline {
    // 省略

    isEditable(hasAdminRole: boolean): boolean {
        if (hasAdminRole) {
            return true;
        } else {
            return this.isOwnPolyline;
        }
    }
}

// モデルやメソッドのテストを書く部分
if (import.meta.vitest) {
    const { it, describe, expect } = import.meta.vitest;

    describe('CustomPolylineを編集可能かどうかを判定するメソッドであるisEditableのテスト', () => {
        // 省略
    });
}

これでテストをすることが可能です。 if(import.meta.vitest)を記載しているのはプロダクションビルド時にテストコードがバンドルファイルに含まれないようにするためです。

注意点

In-Source Testingを用いてテストを実行する際は、tsconfig.jsonが存在する階層で実行してください。 tsconfig.jsonの設定が読み込めずに、失敗してしまいます。

最後に

今回は@soleo/google-maps-vitest-mocksを使って、Vitestを使った場合でもGoogle Maps APIを使ったテストコードを書いてみました。

Jestと書き方が若干異なる部分もありますが、大きな違いではないので、スムーズに書けると思います。Jestよりも早く実行できます。Viteを使った環境下ならばぜひ使っていきたいですね。

OPTiMにはフロントエンドだけなく、多方面で高い技術力を持つエンジニアが多く在籍しています。そんな方々と共に「未来により良い影響を与える」プロダクトの開発を進めていきませんか? ご興味のある方は、ぜひ一度ご連絡ください。