11日目: ユニットテスト
今日は、完全に異なる内容: 自動化されたテストを語ります。このトピックの内容はとても大きいので、すべての内容をカバーするのに2日まるごとかかります。
Arkのすべてのテストは、慣習的にプロジェクトのt/
ディレクトリに設置されます。
今日はユニットテストをカバーし、明日は機能テストに専念します。
ユニットテスト
ユニットテストを書くのはWeb開発のベストプラクティスの中で実行するのがもっとも難しいことです。 Web開発者は作品をテストすることに本当に慣れていないのと、たくさんの疑問がわき上がります: 機能を実装する前にテストを書かなければならないのか?何をテストする必要があるのか?テストはすべての単独の~エッジケース~をカバーする必要があるのか? すべてにおいてよいテストをできる方法は?しかし通常、最初のテストははるかに基本的です: どこで始めるのか?
私たちがテストを強く推奨しているとしても、Arkのアプローチは実践的です: テストを何もしないよりもしたほうが常によいです。テストなしのコードがすでにたくさんありますか?問題ありません。テストの利点から恩恵を受けるためにフルテストスイートを用意する必要はありません。コードでバグを見つけたときにテストを追加することから始めます。時間が経過して、あなたのコードはよりよいものになり、~コードカバレッジ~は上昇し、テストにより自信を持つようになります。実践的なアプローチを始めることで、 時間とともにテストがより快適になります。次のステップは新しい機能に対してテストを書くことです。すぐに、テストがやみつきになりますよ。
たいていのテストライブラリの問題は急激な学習曲線です。
Arkを使ったプロジェクトのテストには、CPAN上にある数多のTestモジュールのなかから好きな物を使う事ができます。
テストを書く作業を簡単にするためにArkがとてもシンプルなテストライブラリである Test::More
を使用する事ができるのはそういうわけです。
前準備
テスト中に作られるデータが本番のデータベースに書き込まれてしまったら大問題です。 確実なテストを行うために、データベースの内容が適切に初期化された状態でテストがスタートする事を保証する必要もあります。
これらの問題を解決するために、テストの事前処理を行うための Jobeet::Test モジュールを作りましょう。 テスト用のデータベースにはSQLiteを使用します。
package Jobeet::Test;
use Ark 'Test';
use File::Temp qw/tempdir/;
use Jobeet::Models;
sub import {
my ($class, $app, %options) = @_;
$app ||= 'Jobeet';
{
my $dir = tempdir( CLEANUP => 1 );
models('conf')->{database} = [
"dbi:SQLite:$dir/jobeet-test-database.db", undef, undef,
{ sqlite_unicode => 1, ignore_version => 1 },
];
models('Schema')->deploy;
}
@_ = ($class, $app, %options);
goto $class->can('SUPER::import');
}
1;
テストの先頭で use Jobeet::Test;
すると、テスト用の新しいデータベースが自動的に作成され、接続先に設定されます。
新しく作成されたテスト用のデータベースは、テストスクリプトが終了すると自動的に破棄されるので、本番のデータを壊す事無くテストを行う事ができるようになります。
初めてのテスト
早速、Jobeet::Test を使ってアプリケーションのテストを書いていきたいところですが、テストスクリプトが意図せずに本番のデータベースにつながってしまったら問題です。安心のために、まずは Jobeet::Test のテストを書く事にしましょう。 テストを書く事で Jobeet::Test がしっかりと動作している確証と、バグの無いコードを書いたという自信を得る事ができます。
ここで一つ注意しておく事があります。 今回は、Jobeet::Test を書いた後にテストを書きますが、本来なら先にテストを書いた後に実装をする -テスト駆動開発(TDD)- をすべき所です。
Test::More
テストには Test::More
モジュールを使用します。
Test::More
はとてもシンプルで扱いやすい、perlのテスト用モジュールです。
Test::More
はいくつかのテスト用関数を提供します。
その中から特に良く使われる物をリストアップします:
メソッド | 説明 |
---|---|
ok($test) |
条件をテストしてtrueであれば通る |
is($value1, $value2) |
2つの値を比較してそれらが等しい場合に通る |
isnt($value, $value2) |
2つの値を比較しそれが等しくない場合に通る |
like($string, $regexp) |
文字列を正規表現でテストする |
unlike($string, $regexp) |
文字列を正規表現にマッチしないことをチェックする |
is_deeply($array1, $array2 |
2つの配列 or 連想配列が同じ値を持っていることをチェックする |
jobeet_test.t
簡単なTest::More
の説明が終わった所で、実際にテストを書いてみましょう。
Jobeet
プロジェクトのテストは、共通して次のモジュールを使います。以降、省略する部分もあります。
use strict;
use warnings;
use Test::More;
use Jobeet::Test;
use Jobeet::Models;
Jobeet::Test
が use
された後に、設定ファイルのデータベース接続先がテスト様に作られた物になっているかを確かめます。
jobeet_test.t
を t/
ディレクトリ以下に作成し、以下のスクリプトを書き込みます。
use strict;
use warnings;
use Test::More tests => 1;
use Jobeet::Test;
use Jobeet::Models;
like models('conf')->{database}[0], qr{dbi:SQLite:/.+jobeet-test-database\.db}, 'connect mock database after "use Jobeet::Test"';
テストを実行する
テストを実行するには prove コマンドを使います。
prove コマンドは Test::Harness モジュールがインストールされていれば、既にあなたのマシンに入っています。
大抵の環境で、既にインストールされていると思いますが、インストールされていない場合は、一日目に行ったように
$ cpanm Test::Harness
を実行して、Test::Harness をインストールしましょう。
prove コマンドには、よく使われるいくつかのオプションがあります:
オプション | 説明 |
---|---|
-l | 実行時にlibディレクトリをライブラリパスに加えます |
-r | テストを再帰的に実行しまう |
-v | 出力を冗長にします |
それでは、先程書いた jobeet_test.t を実行してみましょう!
Jobeetプロジェクトのrootディレクトリに移動して、prove -lr t/jobeet_test.t
を実行します。
$ prove -l t/01_jobeet_test.t
t/jobeet_test.t .. ok
All tests successful.
Files=1, Tests=1, 1 wallclock secs ( 0.02 usr 0.01 sys + 0.51 cusr 0.06 csys = 0.60 CPU)
Result: PASS
全てのテストが実行されて、緑色の文字があなたに全てのテストが pass した事を教えてくれます。
このテストで、Jobeet::Test モジュールを使用するとデータベースの接続先がテスト用のdbになっている事を確認できました。 これで安心してプロジェクトのテストを書いていく事ができます。
Schema クラスのテストを書く
テストを書くための下準備が終わったので、今までのチュートリアルに登場した順に沿ってテストを書いていく事にしましょう。
Jobeet::Schema::Result::Job
3日目に登場した Jobeet::Schema::Result::Job
のテストから書いていきます。
基本的な所からテストを始める事が好ましいので、最初にデータの作成が正しく行われるかをテストすることにしましょう。
新しい category
を作成して、それに付随する job
を作ります。
新しくできた job
は Jobeet::Schema::Result::Job
オブジェクトであるべきなので、先程 Test::More
の説明で出て来た isa_ok
を使用して確認します。
コードは次のようになります。
use strict;
use warnings;
use Test::More;
use Jobeet::Test;
use Jobeet::Models;
{
my $new_category = models('Schema::Category')->create({ name => 'Programming' });
my $new_job = $new_category->add_to_jobs({
type => 'full-time',
company => 'Sensio Labs',
logo => 'sensio-labs.gif',
url => 'http://www.sensiolabs.com/',
position => 'Web Developer',
location => 'Paris, France',
description => q[You've already developed websites with symfony and you want to work with Open-Source technologies. You have a minimum of 3 years experience in web development with PHP or Java and you wish to participate to development of Web 2.0 sites using the best frameworks available.],
how_to_apply => 'Send your resume to fabien.potencier [at] sensio.com',
is_public => 1,
is_activated => 1,
token => 'job_sensio_labs',
email => 'job@example.com',
});
isa_ok $new_job, 'Jobeet::Schema::Result::Job';
}
done_testing;
それでは実行してみましょう。
$ prove -l t/02_jobeet_schema_result_job.t
t/jobeet_schema_result_job.t .. ok
All tests successful.
Files=1, Tests=1, 2 wallclock secs ( 0.02 usr 0.01 sys + 0.55 cusr 0.07 csys = 0.65 CPU)
Result: PASS
全てのテストをPASSしました。まだテストは1つしかありませんが、All tests successfulの文字が誇らしいです。
次に、正しく作成される事が確認できた$new_job
に対して、その他のテストをしていきます。
3日目で Jobeet::Schema::ResultBase
に追加した、created_at
や updated_at
への日付の自動更新が、問題なく動作しているかのテストを追加しましょう。
isa_ok $new_job->created_at, 'DateTime';
isa_ok $new_job->updated_at, 'DateTime';
7日目で Jobeet::Schema::Result::Job
に追加した expires_at
の日付の自動挿入も問題なく行われているかテストしましょう
isa_ok $new_job->expires_at, 'DateTime';
それでは、実行してみます。
$ prove -l t/02_jobeet_schema_result_job.t
t/jobeet_schema_result_job.t .. ok
All tests successful.
Files=1, Tests=4, 1 wallclock secs ( 0.02 usr 0.01 sys + 0.55 cusr 0.07 csys = 0.65 CPU)
Result: PASS
おめでとうございます!! 全てのテストが通りました。
良い区切りなので、testsの数が 4 だということをTest::More
に教えてあげます。
最終的に 02_jobeet_schema_result_job.t
は次のようになりました。
use strict;
use warnings;
use Test::More tests => 4;
use Jobeet::Test;
use Jobeet::Models;
{
my $new_category = models('Schema::Category')->create({ name => 'Programming' });
my $new_job = $new_category->add_to_jobs({
type => 'full-time',
company => 'Sensio Labs',
logo => 'sensio-labs.gif',
url => 'http://www.sensiolabs.com/',
position => 'Web Developer',
location => 'Paris, France',
description => q[You've already developed websites with symfony and you want to work with Open-Source technologies. You have a minimum of 3 years experience in web development with PHP or Java and you wish to participate to development of Web 2.0 sites using the best frameworks available.],
how_to_apply => 'Send your resume to fabien.potencier [at] sensio.com',
is_public => 1,
is_activated => 1,
token => 'job_sensio_labs',
email => 'job@example.com',
});
isa_ok $new_job, 'Jobeet::Schema::Result::Job';
isa_ok $new_job->created_at, 'DateTime';
isa_ok $new_job->updated_at, 'DateTime';
isa_ok $new_job->expires_at, 'DateTime';
}
done_testing;
Jobeet::Schema::Result::Category
続いてJobeet::Schema::Result::Category
のテストも行います。
8日目でslug
カラムを追加し、更新される度に自動的に更新されるようにしたのを覚えているでしょうか?
幸いなことに、作業をした日からそれほど時間が経っていないので覚えていると思います。しかし、これが半年後だったらどうでしょうか?自信を持ってどのように自動更新されるかを説明できるでしょうか?
あなたがアインシュタインもビックリの天才なら即答する事も可能かもしれませんが、大半の人はそうでないと思います。
あなた自身のために、そして将来このプロジェクトを引き継ぐかもしれない前途ある若者のために、テストを書くことにしましょう。
テストを書いておけば、DBIx::Class
に明るくない若者が、不本意に Jobeet::Schema::Result::Category
の insert
, update
メソッドを書き換えてしまったとしても、テストが通らなくなるので間違いに気付くようになります。
若者は喜び、Jobeet
を利用しているユーザーはサービスが安定して動いてる事に満足を得てくれるでしょう。
さあ、テストを書いていきましょう。
01_jobeet_schema_result_category.t
ファイルを作り、そこに Jobeet::Schema::Result::Category
のテストを書いていきます。
まず始めに、insert
時に slug
カラムが適切に作られているかをテストします。
use strict;
use warnings;
use Test::More;
use Jobeet::Test;
use Jobeet::Models;
{
my $new_category = models('Schema::Category')->create({
name => 'CamelCase',
});
is $new_category->slug, 'camel_case', 'slug column: create slug column automatically.';
}
done_testing;
実行してみます。
$ prove -lr t/03_jobeet_schema_result_category.t
t/03_jobeet_schema_result_category.t .. ok
All tests successful.
Files=1, Tests=1, 1 wallclock secs ( 0.03 usr 0.01 sys + 0.50 cusr 0.08 csys = 0.62 CPU)
Result: PASS
問題ないですね。
update
時に slug
カラムが自動的に更新される動作のテストも追加します。
$new_category->name('UpdatedCategoryName');
$new_category->update;
is $new_category->slug, 'updated_category_name', 'slug column: update slug column automatically';
再度、実行してみます。
$ prove -lr t/jobeet_schema_result_category.t
t/jobeet_schema_result_category.t .. ok
All tests successful.
Files=1, Tests=2, 1 wallclock secs ( 0.03 usr 0.00 sys + 0.55 cusr 0.08 csys = 0.66 CPU)
Result: PASS
最後にテストの数をTest::More
に教えてあげます。
最終的に 02_jobeet_schema_result_category.t
のコードは、次のようになりました。
use strict;
use warnings;
use Test::More tests => 2;
use Jobeet::Test;
use Jobeet::Models;
{
my $new_category = models('Schema::Category')->create({
name => 'CamelCase',
});
is $new_category->slug, 'camel_case', 'slug column: create slug column automatically';
$new_category->name('UpdatedCategoryName');
$new_category->update;
is $new_category->slug, 'updated_category_name', 'slug column: update slug column automatically';
}
done_testing;
続いて7日目で作成したJobeet::Schema::ResultSet::Category
のテストを書いて行きます。
Jobeet::Schema::ResultSet::Category
04_jobeet_schema_resultset_category.t
ファイルを作り、Jobeet::Schema::ResultSet::Category
のテストを書いていきます。
まずはテストに使うデータを作成します。
## create test data
my $programming_category =
models('Schema::Category')->create({ name => 'Programming' });
my $programming_job = $programming_category->add_to_jobs({
type => 'full-time',
company => 'Sensio Labs',
logo => 'sensio-labs.gif',
url => 'http://www.sensiolabs.com/',
position => 'Web Developer',
location => 'Paris, France',
description => q[You've already developed websites with symfony and you want to work with Open-Source technologies. You have a minimum of 3 years experience in web development with PHP or Java and you wish to participate to development of Web 2.0 sites using the best frameworks available.],
how_to_apply => 'Send your resume to fabien.potencier [at] sensio.com',
is_public => 1,
is_activated => 1,
token => 'job_sensio_labs',
email => 'job@example.com',
});
get_with_jobs
メソッドが Jobeet::Schema::ResultSet::Category
を返すかのテストをします。返り値の型をチェックする事は有用です。
isa_ok models('Schema::Category')->get_with_jobs, 'Jobeet::Schema::ResultSet::Category', 'get_with_jobs: returning "Jobeet::Schema::ResultSet::Category" object';
get_with_jobs
の返す ResultSet
に $programming_job
が含まれている筈なので、そのテストも追加しましょう。
my $first_job = models('Schema::Category')->get_with_jobs->first;
is $first_job->id, $programming_job->id, 'get_with_jobs: got $programming_job';
7日目に実装した通り、get_with_jobs
は少なくとも1つの利用可能な求人から全てのカテゴリを取得するメソッドです。
言い換えると、期限が切れているjob
しかないカテゴリは習得しない、ということになります。
期待通りの動作をするかテストしましょう。
DateTime
モジュールを使うのでコードの先頭に use DateTime;
を追加してください。
my $yesterday = models('Schema')->now->subtract( days => 1 );
# Now, $programming_job expires
$programming_job->update({ expires_at => $yesterday });
is models('Schema::Category')->get_with_jobs->count, 0, 'get_with_jobs: expired job is not appeared';
テストを実行します。
prove -l t/04_jobeet_schema_resultset_category.t
t/jobeet_schema_resultset_category.t .. ok
All tests successful.
Files=1, Tests=3, 1 wallclock secs ( 0.02 usr 0.00 sys + 0.55 cusr 0.07 csys = 0.64 CPU)
Result: PASS
全てのテストが成功しました。
Jobeet::Schema::ResultSet::Category
のテストは完了としましょう。
テストの数を Test::More
に教えてあげます。
jobeet_schema_resultset_category.t
の全コードは、次のようになりました。
use strict;
use warnings;
use Test::More tests => 3;
use Jobeet::Test;
use Jobeet::Models;
use DateTime;
{
## create test data
my $programming_category =
models('Schema::Category')->create({ name => 'Programming' });
my $programming_job = $programming_category->add_to_jobs({
type => 'full-time',
company => 'Sensio Labs',
logo => 'sensio-labs.gif',
url => 'http://www.sensiolabs.com/',
position => 'Web Developer',
location => 'Paris, France',
description => q[You've already developed websites with symfony and you want to work with Open-Source technologies. You have a minimum of 3 years experience in web development with PHP or Java and you wish to participate to development of Web 2.0 sites using the best frameworks available.],
how_to_apply => 'Send your resume to fabien.potencier [at] sensio.com',
is_public => 1,
is_activated => 1,
token => 'job_sensio_labs',
email => 'job@example.com',
});
## do test
{
isa_ok models('Schema::Category')->get_with_jobs, 'Jobeet::Schema::ResultSet::Category', 'get_with_jobs: returning "Jobeet::Schema::ResultSet::Category" object';
}
{
my $first_job = models('Schema::Category')->get_with_jobs->first;
is $first_job->id, $programming_job->id, 'get_with_jobs: got $programming_job';
}
{
my $yesterday = models('Schema')->now->subtract( days => 1 );
# Now, $programming_job expires
$programming_job->update({ expires_at => $yesterday });
is models('Schema::Category')->get_with_jobs->count, 0, 'get_with_jobs: expired job is not appeared';
}
}
done_testing;
Jobeet::Schema::Result::Category 再び
7日目で、カテゴリResultオブジェクトにアクティブな求人を返す get_active_jobs
メソッドを追加しました。
get_active_jobs
に期待する振る舞いは次のようになります:
- 期限切れでない
job
のみを返す - 作成日時(
created_at
)で降順にソートされている - 第一引数で取得する行数を指定できる
それでは上の項目が満たされているかのテストを、jobeet_schema_result_category.t
に追加していきましょう。
まずはテスト用のデータを作ります。別ファイルにする方法もありますが、今回のようにユニットテストの場合はテスト用のデータがソース中に含まれている方が理解が容易なのでそうします。
$design_job_1
, $design_job_2
, $design_job_3
と3つの Design
カテゴリーの job
を作ります。
my $job_rs = models('Schema::Job');
## create test data
my $design_category = models('Schema::Category')->create({ name => 'Design' });
my $design_job_1 = $job_rs->create({
category_id => $design_category->id,
company => "Company 1",
position => 'Web Designer',
location => 'Paris, France',
description => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.',
how_to_apply => "Send your resume to lorem.ipsum [at] company_1.sit",
is_public => 1,
is_activated => 1,
token => "job_1",
email => 'job@example.com',
});
my $design_job_2 = $job_rs->create({
category_id => $design_category->id,
company => "Company 2",
position => 'Web Designer',
location => 'Paris, France',
description => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.',
how_to_apply => "Send your resume to lorem.ipsum [at] company_2.sit",
is_public => 1,
is_activated => 1,
token => "job_2",
email => 'job@example.com',
});
my $design_job_3 = $job_rs->create({
category_id => $design_category->id,
company => "Company 3",
position => 'Web Designer',
location => 'Paris, France',
description => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.',
how_to_apply => "Send your resume to lorem.ipsum [at] company_3.sit",
is_public => 1,
is_activated => 1,
token => "job_3",
email => 'job@example.com',
});
今回のテストから、説明文字列に日本語を使用する事にします。
Jobeet
ではutf-8でプログラムを書いているので、説明文字列に日本語を使用するために use utf8;
と binmode(Test::More->builder->$_, ':utf8') for qw/failure_output output todo_output/;
をテストに追加します。
よってテストの上部は
use strict;
use warnings;
use utf8;
use Test::More tests => 2;
use Jobeet::Test;
use Jobeet::Models;
binmode(Test::More->builder->$_, ':utf8') for qw/failure_output output todo_output/;
になります。
それでは、日本語を使いつつ 期限切れでない job のみを返す の部分のテストをしましょう。
3つの job
を登録したので、 $design_category->get_active_jobs->count
は3を返す筈です。
is $design_category->get_active_jobs->count, 3, 'get_active_jobs: 3つ登録したjobの習得';
$design_job_2
の expires_at
を昨日に設定すると期限切れになるので $design_category->get_active_jobs
の返す結果セットには含まれなくなり $design_category->get_active_jobs->count
は2を返す筈です。
my $yesterday = models('Schema')->now->subtract( days => 1 );
$design_job_2->update({ expires_at => $yesterday });
is $design_category->get_active_jobs->count, 2, 'get_active_jobs: $design_job_2 が期限切れになったので get_active_jobs の返り値に含まれなくなった';
次は、 作成日時(created_at)で降順にソートされている のテストです。
$design_job_1
の 作成日時
を現在に、$design_job_3
の 作成日時
を一時間前に設定します。
この状態で $design_category->get_active_jobs->first
を実行すると、時間軸で後に作られた $design_job_1
が返ってくる筈です。
my $now = models('Schema')->now;
my $one_hour_ago = $now->clone->subtract( hours => 1);
my $tomorrow = $now->clone->add( days => 1 );
$design_job_1->update({ created_at => $now });
$design_job_3->update({ created_at => $one_hour_ago });
{
my $first_job = $design_category->get_active_jobs->first;
is $first_job->id, $design_job_1->id, 'get_active_jobs: $design_job_1が今、$design_job_3が1時間前に作られたので、$design_category->get_active_jobs->first は $design_job_1';
}
$design_job_3
の 作成日時
を一日後にした場合は $design_category->get_active_jobs->first
が $design_job_3
を返す筈です。
$design_job_3->update({ created_at => $tomorrow });
{
my $first_job = $design_category->get_active_jobs->first;
is $first_job->id, $design_job_3->id, 'get_active_jobs: $design_job_3 が $design_job_1 より後に作られた事になったので、$design_category->get_active_jobs->first は $design_job_3';
}
最後に 第一引数で取得する行数を指定できる のテストをします。
{
is $design_category->get_active_jobs->count, 2, 'get_active_jobs: $design_categoryのアクティブなjobは2つ';
is $design_category->get_active_jobs({ rows => 1 })->count, 1, 'get_active_jobs: rowsパラメータで 1 を指定したので 1つだけ習得';
}
テスト数が増えたので、Test::More
に数を教えてあげましょう。use Test::More tests => 8;
に変更します。
最終的に jobeet_schema_result_category.t
の全コードは次のようになりました。
use strict;
use warnings;
use utf8;
use Test::More tests => 8;
use Jobeet::Test;
use Jobeet::Models;
use DateTime;
binmode(Test::More->builder->$_, ':utf8') for qw/failure_output output todo_output/;
{
my $new_category = models('Schema::Category')->create({
name => 'CamelCase',
});
is $new_category->slug, 'camel_case', 'slug column: create slug column automatically';
$new_category->name('UpdatedCategoryName');
$new_category->update;
is $new_category->slug, 'updated_category_name', 'slug column: update slug column automatically';
}
{
my $job_rs = models('Schema::Job');
## create test data
my $design_category = models('Schema::Category')->create({ name => 'Design' });
my $design_job_1 = $job_rs->create({
category_id => $design_category->id,
company => "Company 1",
position => 'Web Designer',
location => 'Paris, France',
description => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.',
how_to_apply => "Send your resume to lorem.ipsum [at] company_1.sit",
is_public => 1,
is_activated => 1,
token => "job_1",
email => 'job@example.com',
});
my $design_job_2 = $job_rs->create({
category_id => $design_category->id,
company => "Company 2",
position => 'Web Designer',
location => 'Paris, France',
description => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.',
how_to_apply => "Send your resume to lorem.ipsum [at] company_2.sit",
is_public => 1,
is_activated => 1,
token => "job_2",
email => 'job@example.com',
});
my $design_job_3 = $job_rs->create({
category_id => $design_category->id,
company => "Company 3",
position => 'Web Designer',
location => 'Paris, France',
description => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.',
how_to_apply => "Send your resume to lorem.ipsum [at] company_3.sit",
is_public => 1,
is_activated => 1,
token => "job_3",
email => 'job@example.com',
});
## do testing
is $design_category->get_active_jobs->count, 3, 'get_active_jobs: 3つ登録したjobの習得';
{
my $yesterday = models('Schema')->now->subtract( days => 1 );
$design_job_2->update({ expires_at => $yesterday });
is $design_category->get_active_jobs->count, 2, 'get_active_jobs: $design_job_2 が期限切れになったので get_active_jobs の返り値に含まれなくなった';
}
{
my $now = models('Schema')->now;
my $one_hour_ago = $now->clone->subtract( hours => 1);
my $tomorrow = $now->clone->add( days => 1 );
$design_job_1->update({ created_at => $now });
$design_job_3->update({ created_at => $one_hour_ago });
{
my $first_job = $design_category->get_active_jobs->first;
is $first_job->id, $design_job_1->id, 'get_active_jobs: $design_job_1が今、$design_job_3が1時間前に作られたので、$design_category->get_active_jobs->first は $design_job_1';
}
$design_job_3->update({ created_at => $tomorrow });
{
my $first_job = $design_category->get_active_jobs->first;
is $first_job->id, $design_job_3->id, 'get_active_jobs: $design_job_3 が $design_job_1 より後に作られた事になったので、$design_category->get_active_jobs->first は $design_job_3';
}
}
{
is $design_category->get_active_jobs->count, 2, 'get_active_jobs: $design_categoryのアクティブなjobは2つ';
is $design_category->get_active_jobs({ rows => 1 })->count, 1, 'get_active_jobs: rowsパラメータで 1 を指定したので 1つだけ習得';
}
}
done_testing;
それでは実行してみましょう。
$ prove -l t/jobeet_schema_result_category.t
t/jobeet_schema_result_category.t .. ok
All tests successful.
Files=1, Tests=8, 1 wallclock secs ( 0.02 usr 0.01 sys + 0.58 cusr 0.07 csys = 0.68 CPU)
Result: PASS
全てのテストが成功して、緑色の文字列があなたを祝福してくれました。 あなたのアプリケーションは確実に堅守な物になりました。素晴らしいです。
全てのテストを実行する
今日頑張ってテストを書いたので、t/
ディレクトリの下には
- 00_compile.t
- 01_jobeet_test.t
- 02_jobeet_schema_result_job.t
- 03_jobeet_schema_result_category.t
- 04jobeet_schema_resultset_category.t
の5つのテストがある状態になりました。 全てのテストが成功し、プロジェクトが健全な状態にあるか確かめましょう。
prove
コマンドに t
ディレクトリを引数として実行するとディレクトリ以下のテストを全て実行する事ができます。
今回は使っていませんが、サブディレクトリにあるテストも実行させる場合には、先程説明した -r
オプションを使用します。
$ prove -l t
...
All tests successful.
Files=5, Tests=17, 3 wallclock secs ( 0.04 usr 0.02 sys + 2.34 cusr 0.34 csys = 2.74 CPU)
Result: PASS
全てのテストを pass
した事を確認したので、今日はここまでにしましょう。
また明日
アプリケーションのテストが簡単でなくても、今日のチュートリアルをスキップしたい方がいらっしゃるのはわかります。取り組んでいただけば幸いです。
Arkを受け入れることはArkが提供するすばらしい機能すべてを学ぶことだけでなく、Arkが提唱する開発の~哲学~と~ベストプラクティス~でもあります。テストはそれらの1つです。遅かれ早かれ、ユニットテストは時間の節約になります。これらはコードへの確固たる信頼と恐れずにリファクタリングできる自由を与えてくれます。ユニットテストは何かが壊れているときに警告してくれる安全な護衛です。
明日はjobとcategoryモジュール用の機能テストを書きます。それまでは、Jobeetモデルクラス用のユニットテストをさらに書くための時間をとってください。