Skip to content

Instantly share code, notes, and snippets.

@mihyaeru21
Created April 18, 2016 07:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mihyaeru21/65eeffc9b0b242e89218789ac7402684 to your computer and use it in GitHub Desktop.
Save mihyaeru21/65eeffc9b0b242e89218789ac7402684 to your computer and use it in GitHub Desktop.

APIのテスト

テストの重要性は、改めて説明しなくてもわかると思うので割愛します。 ここではAPIエンドポイントに対するテストを書く方法を示します。

テストを走らせるための下準備

不要ファイルの削除

例によって雛形作成時にテストファイルが作られているので下記のファイルをまとめて削除します。

  • t/00_compile.t
  • t/01_root.t
  • t/02_mech.t
  • t/03_assets.t
  • t/06_jshint.t

ファイル変更: config/test.pl

テストで接続するデータベースの名前をnopasteからnopaste_testに変更します。

"dbi:mysql:database=nopaste_test;host=localhost;", 'root', '', {

設定ファイルは、初めて$c->configを呼んだ時(この場合のデフォルトをdevelopment)もしくは、script/nopaste-serverの内部でオプションが提供された場合に読み込まれます。 テストの場合は、t::Util内で環境変数のPLACK_ENVtestという文字列を入れており、これによってテスト用の設定ファイルが読み込まれるようになります。 詳しく知りたい場合は、Amon2::load_configあたりを読んでみてください。

ファイル追加: sal/cleanup_for_test.sql

テスト時はTest::mysqldを使用してMySQLを新たに立ち上げ、まっさらな状態で始めるのが良いのですが、Mac環境ではTest::mysqldを動作させるのが面倒なため別途データベースを作成します。 そして、t::Utilをインポートするタイミングでデータベースを削除して再度作り直します。

DROP DATABASE IF EXISTS nopaste_test;
CREATE DATABASE nopaste_test;
USE nopaste_test;
DROP TABLE IF EXISTS text;

本来なら、Memcachedについてもテスト時の動作をケアすべきですが、今回テストする範囲ではMemcachedは呼び出されないため割愛しました。 気になる場合はTest::Memcachedのドキュメントを参照してください。

ファイル変更: t/Util.pm

ファイルの一番下に、もともとSQLiteを初期化するコードが書かれています。 SQLiteの分を削除し、MySQLを初期化するように変更します。

最後のNoPaste->bootstrapは、Webアプリとして読み込まれていない場合(テスト内のリクエスト処理部以外)でもNoPasteのコンテキストを作成し、DB等にアクセスするために必要な処理です。

use NoPaste;
{
    system("mysql -uroot < sql/cleanup_for_test.sql");
    system("mysql -uroot nopaste_test < sql/mysql.sql");

    NoPaste->bootstrap;
}

これでテストを記述する準備が整いました。

APIのテスト

ファイル追加: t/api/text.t

use strict;
use warnings;
use utf8;

use t::Util;
use Test::More;
use Test::More::UTF8;
use Test::Pretty;
use Test::Deep;
use Test::Deep::JSON;

use Plack::Test;
use Plack::Util;
use HTTP::Request::Common;

use NoPaste::Repository::Text;

my $test = Plack::Test->create(Plack::Util::load_psgi 'script/nopaste-server');

subtest 'GET /api/text/:id' => sub {
    subtest 'データがない場合' => sub {
        my $res = $test->request(GET '/api/text/1');

        is $res->code, 404 or diag $res->content;
        cmp_deeply $res->content, json({
            status_code => 404,
            message     => 'Not Found.',
        });
    };

    subtest 'データがある場合' => sub {
        NoPaste::Repository::Text->create('test');

        my $res = $test->request(GET '/api/text/1');

        is $res->code, 200 or diag $res->content;
        cmp_deeply $res->content, json({
            id   => 1,
            text => 'test',
        });
    };
};

実行

テストファイルを単独で実行する場合はperlで実行します(proveでも実行できます)。

$ carton exec perl t/api/text.t

特定のディレクトリ以下のテストを全て実行するにはproveを使用します。 ディレクトリを省略した場合、tディレクトリをテストします。 proveの動作や設定についてはドキュメントを参照してください。

$ carton exec prove

演習

POST /api/textPUT /api/text/:idについても同様のテストを記述してください。 丁寧に条件を書くとすると、これくらいになるはずです。

subtest 'POST /api/text/:id' => sub {
    subtest 'textパラメータがない場合' => sub {};
    subtest 'textパラメータがある場合' => sub {};
};

subtest 'PUT /api/text/:id' => sub {
    subtest 'データがない場合' => sub {};
    subtest 'textパラメータがない場合' => sub {};
    subtest 'データがある場合' => sub {};
};

多くのテスト用モジュール、関数が登場しているので、これらのモジュールのドキュメントを読むと理解が進みます。

  • Test::More
    • subtest
    • is
    • diag
    • done_testing
  • Test::Deep
    • cmp_deeply
  • Test::Deep::JSON
    • json
  • Plack::Test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment