目次
1.単体テストとは?
2.単体テストを書くメリット
3.単体テストの作成
4.参考
1. 単体テストとは?
関数や、メソッド単位で動作確認を行う。
2. 単体テストを書くメリット/デメリット
メリット
- モジュールが結合される前にテストができるため、問題の原因特定がしやすい
- コードの作成と同時にテストコードが書いていくため、テストケースが資産として残っている。
つまり、後々の改修がしやすい - PHPUnitを使うことで自動化できる→コマンド一つで動作確認が可能(改修時の確認においても)
デメリット
- 開発者の開発負担が高くなる
3. 単体テストの作成
FeatureとUnit
どちらもtestsディレクトリはいかに存在するディレクトリです。
どちらのディレクトリでもテストを記載できるのですが、どのように使い分けるべきかまとめました。
ディレクトリ | 使い分けについての説明 |
---|---|
Feature | 1つのHTTPリクエストの動作テスト / UnitよりもTest範囲が広い |
Unit | 作成したクラスそれぞれに対応するテストクラス作成(Service、Model、Middlewareなど) / 1つ1つのメソッドなどの動作検証 / (ControllerはFeatureに。) |
以下参考記事です。
実装していく
と、まとめてみたものの実際に実装しないとよく分からないので、
以前、実装したコードを元にどんどんテストしていきます。
- 意識する事 - |
---|
Feature / Unitを使い分けて書く |
メソッド単位〜HTTPリクエストまで幅広くテストコードをどんどん書いていく |
// Featureディレクトリにテストを生成する
$ php artisan make:test Post/ToPostTest
// Unitディレクトリにテストを生成する
$ php artisan make:test Post/ToPostTest --unit
テストを書いていきます。
●<HTTPリクエストテスト>
基本的な機能テスト
<?php
namespace Tests\Feature\Post;
use Tests\TestCase;
class ToPostTest extends TestCase
{
public function testGetPage()
{
$response = $this->get('/post/topost');
$response->assertStatus(302);
}
}
●<データ検索:存在チェック>
存在するもの、しないもの、どちらも検索をかけてチェックしています。
<?php
namespace Tests\Feature\Post;
use Tests\TestCase;
class ToPostTest extends TestCase
{
/**
* 投稿用のお風呂の検索
*
* @return void
*/
public function testSearchBathName()
{
// 都道府県検索
$place = Bath::where('place', '東京都')->first();
$this->assertNotNull($place); // Nullで無ければtrue
$place = Bath::where('place', '韓国')->first();
$this->assertNull($place);
$place = Bath::where('place', 'トウキョウ')->first();
$this->assertNull($place);
$place = Bath::where('place', 'tokyo')->first();
$this->assertNull($place);
$place = Bath::where('place', '東京')->first();
$this->assertNull($place);
// キーワード検索:あいまい検索
$keyword1 = Bath::where('name', 'like', "%ああ%")->first(); // nameカラムに、「あ」があることの確認
$this->assertNotNull($keyword1);
$keyword2 = Bath::where('name', 'like', "%a%")->first(); // nameカラムに、「a」があることの確認
$this->assertNotNull($keyword2);
$keyword3 = Bath::where('name', 'like', "%かかかかかかk%")->first(); // nameカラムに、「かかかかかかk」がないことの確認
$this->assertNull($keyword3);
}
}
何かもっといいやり方がありそう。。。
●<テストデータ投稿テスト>
ファクトリーを作成してテストを行います
$ php artisan make:factory PostFactory
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\Models\Post;
use Faker\Generator as Faker;
use Illuminate\Http\UploadedFile;
$factory->define(Post::class, function (Faker $faker) {
return [
'title' => $faker->name,
'thoughts' => $faker->name,
'main_image_path' => UploadedFile::fake()->create('main.jpeg', 10)->store('uploads'),
'eval_cd' => $faker->randomElement([0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5]),
];
});
<?php
namespace Tests\Feature\Post;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
/**
* お風呂投稿 Featureテスト
*/
class ToPostTest extends TestCase
{
use DatabaseTransactions; // FactoryのテストデータをDBに保存させない
/**
* 投稿画面 投稿処理、投稿後リダイレクト、DB保存テスト
*
* @return void
*/
public function testPost()
{
$user = factory(User::class)->create();
$post = factory(Post::class)->create();
$this->actingAs($user)->get('/post/mypost');
$this->withoutExceptionHandling(); // Exception発生後のスルー回避
$data = [
'title' => $post->title,
'thoughts' => $post->thoughts,
];
$response = $this->withoutMiddleware()->post('/post/topost', $data); // ミドルウェアを通さない
$response->assertRedirect('/post/mypost'); // リダイレクトテスト
$this->assertDatabaseHas('posts', $data); // DBテスト(postsテーブル)
}
}
factory()->create()
・Eloquentのsaveメソッドを使用しデータベースへ保存
・factory()->make()
だと、インスタンスだけ作成
use DatabaseTransactions;
・FactoryのテストデータをDBに保存させないようにしています。
$this->withoutExceptionHandling()
の存在意義について
・記載がないと、テスト対象のスクリプト内でExceptionが発生してもスルーされる
・Webサーバーはステータス500を返しているはずのところを、$response->assertStatus(200)
が通る
・例外処理のテストでもない限り、$this->withoutExceptionHandling()
は記載すべき
4. 参考