こんにちは。よっしーです(^^)
今日は、clock-mockを導入して、任意の時間で動作させる方法についてご紹介します。
前提
この記事は下記の記事をベースにしています。
背景
ローカルで開発中に任意の時間で動作させる必要がありましたので、slope-it/clock-mockを導入したときの設定をご紹介します。
slope-it/clock-mock
“ClockMock”は、PHPの\DateTime(Immutable)オブジェクトや日時関連の関数で使用される現在のタイムスタンプをモックする方法を提供するものであり、uopz拡張(バージョン6.1.1以上)が必要です。
このライブラリは開発とテストのために作成されたものです。本番コードで使用されるクロックサービスを提案することを目指しておらず、テストコード内で現在の時間をモックする必要がある場合にのみ使用することを意図しています。
下記の記事で、このライブラリがどのようにして、なぜ作成されたのかが詳しく説明されています。
要約すると、
本番コードを変更せずにネイティブなPHPの日付と時間の関数やクラスをモックする方法を探していました。また、日付や時刻を扱うためのサードパーティのライブラリを使用することも避けたかったです。 そのため以前はphp-timecop拡張を使用していましたが、PHP 7.4以降に対するサポートが実装されていなかったため、別の方法を探していました。
公式サイトは下記になります。
修正内容
下記のファイルを更新、もしくは、作成します。下記の各セクションに各ファイルの修正内容を記載しています。
modified: app/php-fpm/Dockerfile
modified: app/php-fpm/slim_app/app/routes.php
modified: app/php-fpm/slim_app/composer.json
app/php-fpm/Dockerfile
赤色の文字列を削除して、緑色の文字列を追記しました。
FROM php:8.2-fpm
# apt-get update & install
-RUN apt-get update
+RUN apt-get update \
+ && apt-get install -y \
+ unzip
RUN pecl install redis \
&& docker-php-ext-enable redis
RUN docker-php-ext-install pdo_mysql
+RUN pecl install uopz \
+ && docker-php-ext-enable uopz
+
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug
+COPY --from=composer /usr/bin/composer /usr/bin/composer
+
app/php-fpm/slim_app/composer.json
緑色の文字列を追記しました。
+ "slope-it/clock-mock": "^0.3.8",
参考までに、この行は下記のコマンドで登録しました。
cd app
docker compose run -it --rm php-fpm sh -c "cd /var/www/slim_app; composer require --dev slope-it/clock-mock"
app/php-fpm/slim_app/app/routes.php
緑色の文字列を追記しました。
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\App;
use Slim\Interfaces\RouteCollectorProxyInterface as Group;
+use SlopeIt\ClockMock\ClockMock;
+ $app->get('/testtime', function (Request $request, Response $response) {
+ $data = [];
+
+ $now = new Datetime();
+ $data['01'] = $now->format('Y-m-d H:i:s.u');
+
+ ClockMock::freeze(new \DateTime('2000-01-01 12:00:00'));
+ $now = new Datetime();
+ $data['02'] = $now->format('Y-m-d H:i:s.u');
+
+ ClockMock::reset();
+ $now = new Datetime();
+ $data['03'] = $now->format('Y-m-d H:i:s.u');
+
+ $json = json_encode($data, JSON_PRETTY_PRINT);
+ $response->getBody()->write($json);
+ return $response;
+ });
動作確認
下記のコマンドを実行します。
make setup
make up
下記のコマンドを実行します。
curl -k https://localhost/testtime
下記のようにクエリーの発行が確認できれば成功です。
% curl -k https://localhost/testtime
{
"01": "2023-08-20 08:22:35.730096",
"02": "2000-01-01 12:00:00.000000",
"03": "2023-08-20 08:22:35.735855"
}
解説
uopz
uopz
(User Operations for Zend)は、PHPの拡張モジュールであり、ユーザーがPHPの実行時振る舞いを変更できるようにするための機能を提供します。主に実行時の関数のオーバーライド、メソッドの削除、関数の再定義などの機能を提供します。これにより、テストやデバッグの際に特定の動作を模倣したり、モックしたりすることが容易になります。
以下は、uopz
拡張の主な機能や使用法に関する情報です。
- 関数のオーバーライドと再定義:
uopz
は、既存の関数の動作を変更することができます。オリジナルの関数の代わりにカスタムのコードを実行したり、返り値を変更したりすることが可能です。 - クラスのメソッドの削除と再定義: クラスのメソッドも削除したり再定義したりすることができます。これにより、特定のクラスメソッドをモックしてテストすることができます。
- 関数の動的な追加:
uopz
は、新しい関数を動的に追加する機能も提供します。これは特定の状況で便利であり、特定のテストケースやモックシナリオをサポートします。 - リフレクションの拡張:
uopz
は、リフレクションクラスを拡張し、関数やクラスの詳細な情報を取得するための関数を提供します。 - テストとデバッグ: ユニットテストやデバッグの際に、
uopz
を使用して特定の関数やメソッドの動作を制御し、想定した振る舞いを確認できます。これにより、テストの安定性や再現性を向上させることができます。
uopz
は非常に強力な機能を持っていますが、注意が必要です。適切な使用方法を理解していない場合、コードの可読性や保守性に影響を及ぼす可能性があります。また、uopz
拡張がPHPのバージョンや設定に依存するため、利用する際には互換性やバージョンの確認が必要です。
app/php-fpm/slim_app/app/routes.php
このコードは、PHPアプリケーションの一部として、HTTP GETリクエストに応答するエンドポイントを定義しています。このエンドポイントは /testtime
というパスでアクセス可能であり、リクエストがあった際に特定の時間の情報をJSON形式で返します。以下にコード内の各部分の説明を示します。
- エンドポイントの定義:
$app->get('/testtime', function (Request $request, Response $response) {
// ...
});
この部分は、HTTP GETリクエストを受け取ると実行されるコードを定義しています。$app
はおそらくフレームワーク(おそらくSlim Frameworkなど)のインスタンスを示しているものと思われます。Request
と Response
は、HTTPリクエストとレスポンスを処理するためのオブジェクトです。
- 時間情報の取得と処理
$data = [];
$now = new Datetime();
$data['01'] = $now->format('Y-m-d H:i:s.u');
最初に、空の配列 $data
を用意しています。その後、現在の日時を表す Datetime
オブジェクトを作成し、そのフォーマットを 'Y-m-d H:i:s.u'
に設定して、ミリ秒まで含んだ日時情報を文字列として配列 $data
の要素 '01'
に格納しています。
- 時間のモックと処理
ClockMock::freeze(new \DateTime('2000-01-01 12:00:00'));
$now = new Datetime();
$data['02'] = $now->format('Y-m-d H:i:s.u');
ここでは、ClockMock
クラスを使用して、時間をモックしています。まず、ClockMock::freeze()
メソッドを使用して、指定の日時(2000年1月1日12時00分00秒)を「固定」した状態で時間を凍結しています。その後、新たに Datetime
オブジェクトを作成して、その日時情報を配列 $data
の要素 '02'
に格納しています。これにより、モックされた時間が反映された結果が得られます。
- 時間モックのリセットと処理
ClockMock::reset();
$now = new Datetime();
$data['03'] = $now->format('Y-m-d H:i:s.u');
ここでは、ClockMock
クラスの reset()
メソッドを使用して、時間のモックをリセットしています。その後、再び新たに Datetime
オブジェクトを作成して、その日時情報を配列 $data
の要素 '03'
に格納しています。これにより、リセットされた後の実際の現在の時間が反映された結果が得られます。
- レスポンスの生成
$json = json_encode($data, JSON_PRETTY_PRINT);
$response->getBody()->write($json);
return $response;
最後に、配列 $data
をJSON形式にエンコードして整形しています。それをレスポンスボディに書き込んで、それを返しています。このレスポンスにより、テストしたりデバッグしたりする際に各時点での時間情報が確認できます。
おわりに
今日は、clock-mockを導入して、任意の時間で動作させる方法についてご紹介しました。
本記事でご紹介したソースは、下記のリポジトリにあります。
何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント