LoginSignup
0
0

More than 1 year has passed since last update.

LaravelでMockeryを使った試験の話

Posted at

サービスを試験する時、試験対象サービスから別の連結するサービスがあったとします。
その時はどう試験しますか?
以降の連なるサービスを含めて試験を行うというのは変ですね。
Unit試験は自己完結が範囲だとか愚行します。
以降のサービスに不具合があった場合にはこのサービスの試験がNGになるのは変です。
つまりは以降のサービスをブラックボックスにできる方法がベストです。
次のサービスに引き渡す値の結果が想定した結果を戻るだけでいいのです。

これを解決するのにMockery(mock)を使います。

image.png

mockとは

mockとは… 辞書で調べるとこんな感じです。

まがいの、偽りの、まねごとの

mockオブジェクトとは本来のオブジェクトではなく想定の結果を返すオブジェクトということです。

Mockeryとは

Mockery は、PHPUnit や PHPSpec などのテストフレームワークで使用するための、 シンプルかつ柔軟な PHP モックオブジェクトフレームワークです。

試験対象とmockの概略

今回の試験対象のBooksServiceは著者情報を取得するためにSearchAuthorServiceと連携します。
image.png

今回はこのSearchAuthorServiceをmockにしてBooksServiceを試験します
image.png

SearchAuthorServiceではメソッド searchに書籍名を渡せば著者名を返してくれます
というインタフェイスを定義されています。

image.png

SearchAuthorService mockでは以下の特徴を持っています

SearchAuthorServiceクラスの型をもったオブジェクト
searchメソッドに書籍名を渡せば決められた著者情報を返してくれる

これによりBooksServiceでは、SearchAuthorServiceは想定した動作することで試験は可能になります。

今回の試験対象部分ですが、SearchAuthorServiceのsearchが該当箇所です

try {
    // 著者情報を取得します
    $currentBook->author = $this->authorService->search($book->title);
} catch (Exception $e) {
    Log::debug($e);
}

試験対象ソース : BooksService.php

PHPUnitでmockを作る方法

mockをPHPUnitに実装するには以下の構成となっております

  • mockインスタンスを作る
  • mockメソッドの結果値を指定する
  • たまに、mockメソッドの例外を発生させる

mockを含んだ試験の内容を抜粋します

$authorService = Mockery::mock(SearchAuthorService::class);
$authorService
    ->shouldReceive('search')
    ->with('ドメイン特化言語 パターンで学ぶDSLのベストプラクティス46項目')
    ->andReturn('マーチン ファウラー');
$authorService
    ->shouldReceive('search')
    ->with('3月のライオン(13)')
    ->andReturn('羽海野チカ');
$authorService
    ->shouldReceive('search')
    ->andReturn('不明');
 
$booksService = new BooksService($authorService);
 
$book = $booksService->serve(1 , false);
$this->assertEquals('ドメイン特化言語 パターンで学ぶDSLのベストプラクティス46項目' , $book->title);
$this->assertEquals('マーチン ファウラー', $book->author);

試験全ソース : BooksServiceTest.php

mockのインスタンス作成方法

mockのインスタンスを作成するには普通とは異なります。

本来はこの様にインスタンスを作成してサービスに引き継ぎます。

$authorService = new SearchAuthorService();
$booksService = new BooksService($authorService);

mockの場合はMockeryを使ってmockのクラスを作ります。
それを連携させます。

$authorService = Mockery::mock(SearchAuthorService::class);
$booksService = new BooksService($authorService);

mockのメソッドの結果を指定する

次にどのメソッドから何を返すのかを定義します

$authorService = Mockery::mock(SearchAuthorService::class);
$authorService
    ->shouldReceive('search')
    ->with('ドメイン特化言語 パターンで学ぶDSLのベストプラクティス46項目')
    ->andReturn('マーチン ファウラー');
 
$booksService = new BooksService($authorService);

shouldReceiveで指定したメソッド (search)の引数(with)の場合に何を返すか(andReturn)をそれぞれ定義しています。
引数(with)がない場合は、そのメソッドの全ての結果になります

mock処理で例外を発生させる

メソッドを利用した時に例外を発生させることもできます。

$authorService
    ->shouldReceive('search')
    ->andThrow(new Exception('不明'));

これにより例外処理の試験が楽にできますので結構便利です。

自己満足的にカバレッジレポートが100%近くできますw

image.png

例外部分がカバーできる
image.png

ちなみに
ここまで来てネタバラシですが、今回のSearchAuthorServiceのソースを出しますと…

<?php
 
namespace App\Services;
 
use Exception;
 
/**
 * 著者情報取得サービス
 */
class SearchAuthorService
{
    /**
     * 書籍タイトルから著者を検索します
     * @param string $title 書籍タイトル
     * @return string|null 著者名
     * @throws Exception 何らかの例外
     */
    public function search(string $title) : ?string {
        return null;
    }
}

全ソース : SearchAuthorService.php

なにもできていませんが、BooksServiceは想定した試験ができます。

終いに
今回はLaravelを使ったサービスの連携でMockeryを使ってmock試験の方法を書きました。

前回記事のSeederと合わせればかなりの試験をカバーすることができます。

ついでに今回のmock試験を行うということはSearchAuthorServiceにおいて以下の試験は必ず通ることが前提になります。試験一覧に追加しておきましょう。

$authorService = new SearchAuthorService();
$this->assert('マーチン ファウラー',$authorService->search('ドメイン特化言語 パターンで学ぶDSLのベストプラクティス46項目'));
$this->assert('羽海野チカ',$authorService->search('3月のライオン(13)'));
``
あとは HTTPリクエストとかを隠蔽する方法があればな

```php
$contents = @file_get_contents($url, false, stream_context_create($options));

これを!独立させずに何とかしたいな… w

ちなみに独立させると…

class HTTPAccessService
{
    public function access(string $url , array $options) : ?string {
        return @file_get_contents($url, false, stream_context_create($options));
    }
}

これかな…

参照
プロジェクト : https://github.com/wataru775/learning-leravel/tree/20220427_mock_test

0
0
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
0