LoginSignup
1
0

テストファーストにコードを修正してみるぞ

Posted at

概要

例として 「今が店舗の営業時間内かどうか」 を判定するクラスを用意する。

登場モデル説明

  • PublicHoliday
    • 日付のカラムを持つ祝日テーブルに対応したモデル
  • Store
    • 平日や土日や祝日の営業時間のカラムを持つ店舗テーブルに対応したモデル

コード書けた!

よし、今が店舗の営業時間内かどうかを判定するクラスが出来たぞ!

class StoreOperatingHoursChecker
{
    public function __invoke(Store $store): bool
    {
        if (PublicHoliday::whereDate('date', now()->format('Y-m-d'))->exists()) {
            $openingTime = $store->opening_time_public_holiday;
            $closingTime = $store->closing_time_public_holiday;
        } elseif (now()->isSunday()) {
            $openingTime = $store->opening_time_sunday;
            $closingTime = $store->closing_time_sunday;
        } elseif (now()->isSaturday()) {
            $openingTime = $store->opening_time_saturday;
            $closingTime = $store->closing_time_saturday;
        } else {
            $openingTime = $store->opening_time_workday;
            $closingTime = $store->closing_time_workday;
        }

        return ($openingTime <= now()->toTimeString() && $closingTime > now()->toTimeString());
    }
}

テストコードを書いていこうと思うが、面倒な所がいくつかある

  • 日時の判定がテスト実行日時に依存するから、曜日によって変わる営業時間のテストが出来ない
  • 祝日の判定がデータベースに依存しているからデータを登録しないといけない

この問題を解消していこう。まず判定する日時がnow()に依存しているので、
そこを引き剥がして外側から値を渡すようにしよう

now()ヘルパーの結果に依存してしまっているので、
この依存性を外部から注入するようにします
ここがDI(依存性注入)ポイントです

店舗と日時を引数に受け取る

class StoreOperatingHoursChecker
{
    /**
     * 引数に受け取った日時が営業時間内かどうかを判定する
     *
     * @param  Store  $store
     * @param  Carbon  $datetime
     *
     * @return bool
     */
    public function __invoke(Store $store, Carbon $datetime): bool
    {
        if (PublicHoliday::whereDate('date', $datetime->format('Y-m-d'))->exists()) {
            $openingTime = $store->opening_time_public_holiday;
            $closingTime = $store->closing_time_public_holiday;
        } elseif ($datetime->isSunday()) {
            $openingTime = $store->opening_time_sunday;
            $closingTime = $store->closing_time_sunday;
        } elseif ($datetime->isSaturday()) {
            $openingTime = $store->opening_time_saturday;
            $closingTime = $store->closing_time_saturday;
        } else {
            $openingTime = $store->opening_time_workday;
            $closingTime = $store->closing_time_workday;
        }

        return ($openingTime <= $datetime->toTimeString() && $closingTime > $datetime->toTimeString());
    }
}

よし、now()の部分を引数から受け取る形にしたから好きな日時でテストができるぞ。
後は祝日の判定だな...テスト対象日をわざわざデータベースに登録しないと行けない。

そうだ、引数で渡す日($datetime)が祝日かどうかも外側から渡すようにしてしまおう。

祝日の判定を外部から与える

祝日かどうかの判定ロジックを、このクラスは知らなくていいようにする。

<?php

namespace App\UseCases\Store;

use App\Models\PublicHoliday;
use App\Models\Store;
use Carbon\Carbon;
use Carbon\CarbonImmutable;

class StoreOperatingHoursChecker
{
    /**
     * 引数に受け取った日時が営業時間内かどうかを判定する
     *
     * @param  Store  $store
     * @param  Carbon  $datetime
     * @param  bool  $isPublicHoliday
     *
     * @return bool
     */
    public function __invoke(Store $store, Carbon $datetime, bool $isPublicHoliday): bool
    {
        if ($isPublicHoliday) {
            $openingTime = $store->opening_time_public_holiday;
            $closingTime = $store->closing_time_public_holiday;
        } elseif ($datetime->isSunday()) {
            $openingTime = $store->opening_time_sunday;
            $closingTime = $store->closing_time_sunday;
        } elseif ($datetime->isSaturday()) {
            $openingTime = $store->opening_time_saturday;
            $closingTime = $store->closing_time_saturday;
        } else {
            $openingTime = $store->opening_time_workday;
            $closingTime = $store->closing_time_workday;
        }

        return ($openingTime <= $datetime->toTimeString() && $closingTime > $datetime->toTimeString());
    }
}

これでこのクラスのテストが書きやすくなった。

結論

普通に実装をしていると気がつかないけれども、コードを使ったテストを書く事を頭に置くと色々改善点が見えてくるね!

恩恵

  • 祝日判定の条件を変更する時に、このクラスを書き換える必要が無くなった。
  • 好きな日時で営業時間内判定が行えるようになり、汎用性が高まった。
1
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
1
0