開発者はコードの 100% (またはほぼ 100%) をテストしようとすることがよくあります。どうやら、これがすべてのチームがプロジェクトで達成すべき目標のようですが、私の観点からは、完全にテストする必要があるのはコード全体のうち 1 つの部分、つまりドメインです。
ドメインは基本的に、プロジェクトが実際に何を行うかを定義するコードの一部です。たとえば、エンティティをデータベースに永続化する場合、ドメインはエンティティをデータベース上に永続化する責任はありませんが、永続化されたデータがビジネス モデルに従って意味があるかどうかを確認します。おそらく、データをデータベースに保存するときに、PHP Doctrine などの外部ライブラリを使用することになるでしょう。このライブラリはすでに完全にテストされているため、その動作をテストする必要はありません。 doctrine に正しいデータを渡せば、問題なくデータベースに保存されます。
以下のセクションで示す例は、ドメイン駆動設計がどのように機能するかを示すものではありません。ドメイン駆動設計について非常に詳しく説明している記事がたくさんあります。ドメインを明確に定義して分離すると、テストが簡単になり、アプリケーションの動作に重点を置くことができることを説明します。
この例は Symfony 環境上に構築され、PHPUnit ライブラリを使用していますが、このアイデアはどの言語やフレームワークにも有効です。
アプリケーションが、指定された日付の降水確率に関するデータを返す外部 API に接続していると想像してみましょう。返されるデータは次のようになります:
{ "date" : "2022-12-01", "rain_probability" : 0.75 }
ここで、これらのデータを取得し、次のマッピングに従って分類する必要があります。
そして、次のエンティティによって記述されるデータベース テーブルに結果を保存します:
#[ORM\Entity(repositoryClass: RainMeasure::class)] class RainMeasure { #[ORM\Column] private string $date; #[ORM\Column] private float $probability; #[ORM\Column(length: 10)] private string $label; // Getters and setters // ....... }
外部 API データを取得し、降水確率に応じてラベルを設定し、データベースに保存するハンドラーを作成しましょう。
class RainMeassureHandler { private EntityManagerInterface $em; public function __construct(EntityManagerInterface $em) { $this->em = $em; } public function saveMeasure(array $measureData): void { if($measureData['rain_probability'] < 0.40){ $label = 'LOW'; } elseif ($measureData['rain_probability'] >= 0.40 && $measureData['rain_probability'] < 0.75){ $label = 'MEDIUM'; } else{ $label = 'HIGH'; } $rainMeasure = new RainMeassure(); $rainMeasure->setDate($measureData['date']); $rainMeasure->setProbability($measureData['rain_probability']); $rainMeasure->setLabel($label); $this->em->persist($rainMeasure); $this->em->flush(); } } <p>上記のハンドラーのテストを作成しようとすると、テストしたい動作 (確率値に従ってラベルを設定する) が次のとおりであるため、<strong>EntityManagerInterface</strong> を注入する必要があることがわかります。結果をデータベースに保存する同じハンドラーに結合されます。モックとスタブを使用して <strong>EntityManagerInterface</strong> をロードしてみることもできますが、それは必要ですか?明らかに違います。前に述べたように、降雨確率に応じて正しいラベルを取得する、ドメインに属する動作のテストに重点を置く必要があります。</p> <h2> テストしたいデカップリング動作 </h2> <p>テストを簡素化するために、テストしたい動作を別のクラスに移動します。</p> <pre class="brush:php;toolbar:false"> class RainMeasureLabelHandler { public function getLabelFromProbability(float $prob): string { if($prob < 0.40){ $label = 'LOW'; } elseif ($prob >= 0.40 && $prob < 0.75){ $label = 'MEDIUM'; } else{ $label = 'HIGH'; } return $label; } }
そして、RainMeassureHandler は次のようになります:
class RainMeasureHandler { private EntityManagerInterface $em; public function __construct(EntityManagerInterface $em) { $this->em = $em; } public function saveMeasure(array $measureData): void { $rainMeasureLabelHandler = new RainMeasureLabelHandler(); $label = $rainMeasureLabelHandler->getLabelFromProbability($measureData['rain_probability']); $rainMeasure = new RainMeasure(); $rainMeasure->setDate($measureData['date']); $rainMeasure->setProbability($measureData['rain_probability']); $rainMeasure->setLabel($label); $this->em->persist($rainMeasure); $this->em->flush(); } }
ここで、RainMeasureLabelHandler のテストに集中できます。これはドメインの一部であり、外部レイヤーへの依存関係はありません。テストは次のように簡単です:
他の種類のテストも役立つだろうと言いたいです。おそらく、API があり、データベースや必要なその他のリソースを含むテスト環境を使用して入力と出力をテストしたいと考えています。ただし、ドメインを分離して完全にテストすることを忘れないでください。
以上がドメインに重点を置いてテストを行います。 PHPUnitの例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。