0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

この記事は、「架空プロジェクトを通してシステム開発とドキュメント作成を体験してみる(2022 Late)」の記事の一部です。

概要

ここでは、APIに対する単体テストを実行してみます。
単体テストとはAPI等の最小機能単位に対して正常に動くかテストを行うことです。

単体テストのカテゴリには

  • 正常系テスト(仕様通りに正しく動くか)
  • 異常系テスト(仕様通りに正しくエラーとなるか)
  • 境界値系テスト(○○以上、○○未満等の処理が正しく動作するか)

などがあります。

ここでは正常系、異常系をサンプル程度に行ってみます。

テストは手作業で実施することもできますが、改修が入るごとにテストを手作業で実施するのはとても大変です。
テストを自動化することで、変更があるたびに必要なテストを自動で行えるようになり効率的に開発が行えるようになります。

ここではJavaScriptのテストツールとしてメジャーな「Jest」と、Jest上で実行できるAPIテストツールの「frisby」を利用してみます。

Jestの公式サイト
Jestはシンプルさを重視したテスティングフレームワークで、細かい設定不要で実行できることを目指して作られています。また、テストが失敗したときのエラーメッセージもわかりやすく表示してくれます。

frisby.jsの公式サイト
frisbyはRest APIを簡単にテストすることを目的としたJavaScriptのフレームワークです。

環境構築

node.jsインストール

jestとfrisbyはNode.jsというJavaScriptをサーバやPC等のブラウザ以外の環境で動かすツールに依存しているので、Node.jsをインストールします。

Node.jsの公式サイトからダウンロードしてきます。
LTS版をクリックしてダウンロードします。

LTSとはLong Term Supportの略で、長期間サポート版という感じなので、特に理由が無い限りLTS版を利用するのが無難です。

000001.jpg

Windows版のインストールはこちらを参考にしてください。

開発者はインストーラー版ではなく、複数のNode.jsを切り替えて使えるツール(nodenv)などを介してインストールすることが多いです。

ダウンロードが完了したらダウンロードフォルダを確認し、nodeパッケージファイルをダブルクリックします。

000010.jpg

インストーラが起動するのでガイダンスに沿ってインストールします。

000020.jpg

000030.jpg

000040.jpg

000050.jpg

000060.jpg

000070.jpg

000080.jpg

完了したら、インストーラは不要なのでゴミ箱に入れてしまっていいです。

000090.jpg

確認

ターミナル(Windowsの場合はコマンドプロンプト)を開いてバージョンを確認してみます。

node --version

インストールしたバージョンと同じバージョン情報が返ってきていればインストール成功です。
次に以下のコマンドも実行してみてください。

npm --version

npmはNode.jsで利用する様々な追加機能をインストールするために使うパッケージマネージャというツールです。

実際にはnodeコマンドよりnpmコマンドを利用することの方が多いです。

パッケージのインストール

まず、package.jsonというファイルを生成します。
package.jsonにはnpmでインストールされたパッケージの設定情報が記述されるファイルですが、存在していないとエラーがでる場合があるので生成しておきます。

ターミナルを開いてローカル環境のwebsiteフォルダの中に移動します。

現在の位置を確認するコマンドはpwdと入力してエンターを押すと確認できます。

cd ~/Desktop/website  #デスクトップのwebsiteフォルダに移動

移動できたらwebsiteフォルダ内にpackage.jsonを生成します。

npm init -y #-yオプションをつけるとすべてデフォルトの値の状態でpackage.jsonが生成される

ターミナルが返ってきたらフォルダ内をVSCodeで確認してみます。

ターミナルはまだ使うので閉じずにそのままにしておきます。

package.jsonが追加されているのが確認できます。

000100.jpg

確認ができたのでJestとfrisbyをインストールします。
ターミナルで下記を実行します。

iはinstallのiです。

npm i jest
npm i frisby

この操作はかならずwebsiteディレクトリ内で実施してください。じゃないと思わぬところに変なゴミファイルができてしまいます。
いくつかWARNが出るかもしれませんが、基本的に問題ありません(エラーはダメです)。

しばらくするとインストールが完了してターミナルが返ってきます。
VSCodeで確認すると、package.jsonのdependenciesにfrisbyとjestが追加され、さらにpackage-lock.jsonというファイルとnode_modulesというフォルダが追加されています。

package-lock.json はパッケージインストール時に自動生成されます。パッケージのバージョンがインストールのタイミングによって変わってしまわないよう(基本最新バージョンがインストールされてしまうため)にバージョンを固定して管理してくれるものです。
node_modules はインストールしたパッケージが実際に入っているフォルダです。npmでインストールしたパッケージは全てこの中にはいります。

000110.jpg

テストを書く

テストフォルダ・ファイルの用意

Jestはプロジェクトフォルダの直下に__tests__ フォルダ作を作り、その中にテスト用のスクリプトファイルを入れて管理するか、xxx.test.jsという拡張子をつけたファイル名にするとテストを実行してくれます。

今回のテストは__tests__フォルダで管理していきますので、websiteフォルダ内に__tests__というフォルダを作成します。
__tests__フォルダ内にAPIテストプログラムを記述したファイルを作っていきます。「api.js」というファイルを作成します。
作成すると、下記のようなファイル構造になっています。

000120.jpg

テストプログラム実装

このテストでは4つのテストを実行します。

  • 正常
    • お名前・Email・問合わせ内容が正しく設定されて送信された場合、jsonでmessageが"success!"と返ってくる。
  • 異常:お名前が未設定
    • 必須項目のお名前が未設定(空)の場合、jsonでmessageが"validation error!"と返ってくる(jsonが想定どおり返ってくることを正とする)
  • 異常:Emailの形式が不正
    • Emailの形式が正規表現で指定したルールと一致しない場合、jsonでmessageが"validation error!"と返ってくる(jsonが想定どおり返ってくることを正とする)
  • 異常:問合せ内容が不正
    • 問合わせ内容が10文字以上の場合、、jsonでmessageが"validation error!"と返ってくる(jsonが想定どおり返ってくることを正とする)

実際は上記のパターンの他に、Emailが未設定の場合、問合わせ内容が未設定の場合、全てが未設定の場合、お名前とEmailが未設定の場合・・・、正常な文字数でエラーになるか?など考えられる全てのパターンをテストする必要があります(通常は数十パターンになることが一般的です)。

frisbyの読み込みとAPIのURL設定

まず、ファイルの上部でfrisbyを読み込み、api_urlを指定します。

__tests__/api.js
const frisby = require("frisby"); //frisbyを読み込む

//APIのURLを指定
const api_url = "https://script.google.com/macros/s/{デプロイID}/exec";

正常系

各項目が正常な値の場合のテストを記述します。
frisby.postでAPIに指定したパラメータを投げます。
expectで返ってきた値と期待する値を比較します(例expect(返ってきた値).toEqual(期待値);)。

テキストで返ってきた値(res._body)をJSON.parseしてJSONに戻し、期待値({ "message": "success!" })のJSONと比較します。
JSONの値を比較する場合は「toEqual」または「toStrictEqual」を使います。

api.test.js
//正常系
it("正常系", async () => {

    const res = await frisby.post(api_url, {
        body: "name=aaa&email=test@test.local&body=foooooo",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded"
        }
    });
  
    expect(JSON.parse(res._body)).toEqual({ "message": "success!" });
});

保存してテストを実行してみます。

ターミナルを開いてwebsiteフォルダに移動します。

cd ~/Desktop/website

テストを実行するコマンドは以下の通りです。

npx jest

npxはnode package executerの略でパッケージを実行するツールです。

テストが走り出します。

000130.jpg

少しするとテスト結果が返ってきます。
[PASS]と緑色で表示されその横にPASSしたテストのファイル名が表示されています。
その下にはチェックマークが入り、正常系のテストがクリアしたことがわかります。

000140.jpg

異常系:name不正

同様の手順でお名前が未設定の状態のテストを作成します。
正常系をコピーして、bodyのname=aaaname=に変更、比較するmessageの値をvalidation error!にします。(API作成時にAPI側でバリデーションチェックで引っかかった場合は「validation error!」と返すように設定してました。)

__tests__/api.js
//name不正
it("異常系:name不正", async () => {

    const res = await frisby.post(api_url, {
        body: "name=&email=test@test.local&body=foooooo",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded"
        }
    });

    expect(JSON.parse(res._body)).toEqual({ "message": "validation error!" });
});

保存してターミナルから再度テストを実行します。

npx jest

テスト結果に異常系:name不正を追加され、こちらもPASSしたことがわかります。
000150.jpg
試しに、このテストを正常系の値(name=name=aaaと記述して保存)にしてテストを実行してみます。

body: "name=aaa&email=test@test.local&body=foooooo",

すると、テストはFAILになりました。
正常系にはチェックが入っていますが、異常系:name不正は赤で☓が記されています。
期待値は"message": "validation error!"ですが受け取った値は「"message": "success!"」だということがわかります。

000160.jpg

テストに問題があった場合の挙動がわかったところでname不正のテストの値はname=に戻して保存しておきます。

異常系:Email不正

同様の手順でEmailのテストを記述します。emailの値は「test」にしました。

__tests__/api.js
//Email不正
it("異常系:Email不正", async () => {
    const res = await frisby.post(api_url, {
        body: "name=aaa&email=test&body=foooooo",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded"
        }
    });

    expect(JSON.parse(res._body)).toEqual({ "message": "validation error!" });
});

異常系:問合せ内容不正

同様の手順で問合わせ内容のテストを記述します。
今回は問合わせ内容の値は10文字以下が正常なので、10文字以上にします。

__tests__/api.js
//問い合わせ不正
it("異常系:問合せ内容不正", async () => {
    const res = await frisby.post(api_url, {
        body: "name=aaa&email=test@test.local&body=foooooooooooooo",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded"
        }
    });

    expect(JSON.parse(res._body)).toEqual({ "message": "validation error!" });
});

保存したら、テストを実行してみます。
全てクリアすると以下のような結果が返ってきます。
全てのテストを実行して全てグリーンチェックが入ることが理想です。
000170.jpg

最終的なコード

__tests__/api.js
const frisby = require("frisby"); //frisbyを読み込む

//APIのURLを指定
const api_url = "https://script.google.com/macros/s/{デプロイID}/exec";
//正常系
it("正常系", async () => {

    const res = await frisby.post(api_url, {
        body: "name=aaa&email=test@test.local&body=foooooo",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded"
        }
    });

    expect(JSON.parse(res._body)).toEqual({ "message": "success!" });
});

//name不正
it("異常系:name不正", async () => {

    const res = await frisby.post(api_url, {
        body: "name=&email=test@test.local&body=foooooo",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded"
        }
    });

    expect(JSON.parse(res._body)).toEqual({ "message": "validation error!" });
});

//Email不正
it("異常系:Email不正", async () => {
    const res = await frisby.post(api_url, {
        body: "name=aaa&email=test&body=foooooo",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded"
        }
    });

    expect(JSON.parse(res._body)).toEqual({ "message": "validation error!" });
});

//問い合わせ不正
it("異常系:問合せ内容不正", async () => {
    const res = await frisby.post(api_url, {
        body: "name=aaa&email=test@test.local&body=foooooooooooooo",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded"
        }
    });

    expect(JSON.parse(res._body)).toEqual({ "message": "validation error!" });
});

まとめ

  • 単体テストとはAPI等の機能単位をテストする工程
    • 一般的には開発工程で開発とセットで行う(一定の工数がかかる)
    • 先にテストを記述してから機能を実装するテスト駆動開発(TDD)という品質向上手法もある
  • 手動でもできるが、自動化することが一般的
    • コードを改変したときに(自動)実行させて、バグが発生してないかチェック

ドキュメント作成視点での考察

  • 単体テストの実施の有無、内容はどこにどう記述すべき?
  • テスト結果はどのように評価すればよいか?(それはどこにどう記述すべき?)
  • テストの内容が妥当(十分)であることをどう証明するか?

関連コンテンツ

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?