私の質問は、単純化された例(Javaで)の手元にあります:
public class VehicleRepository {
// DAOs responsible for fetching objects from data store (injected)
private IChassisDAO chassisDAO;
private IEngineDAO engineDAO;
private IWheelDAO wheelDAO;
private IAccessoryDAO accessoryDAO;
public Vehicle getLuxuryCar() {
VehicleBuilder builder = getVehicleBuilder();
builder.setChassis(chassisDAO.findBySize(ChassisSize.NORMAL));
builder.setEngine(engineDAO.findByPower(EnginePower.HIGH));
builder.setWheelTemplate(wheelDAO.findBySize(WheelSize.NORMAL));
builder.setAccessories(accessoryDAO.findByType(Accessory.LUXURY));
return builder.build();
}
public Vehicle getOffroadCar() {
VehicleBuilder builder = getVehicleBuilder();
builder.setChassis(chassisDAO.findBySize(ChassisSize.LARGE));
builder.setEngine(engineDAO.findByPower(EnginePower.HIGH));
builder.setWheelTemplate(wheelDAO.findBySize(WheelSize.LARGE));
return builder.build();
}
// Similar operations ...
}
このクラスの単体テストをどのように行いますか? これについて少し考えてみたところ、いくつかのオプションを特定しました。
ビルダーをモックせずに、返された Vehicle に対してテスト
する インターフェイスの観点からは、これが最適なオプションですが、VehicleRepository での単体テストにはなりません。VehicleBuilder のみをテストするのとは対照的に、VehicleBuilder と一緒に VehicleRepository をテストすることの本当の利点はわかりません。VehicleBuilder には複雑なロジック (アクセサリが存在するかどうかのチェックなど) が含まれる場合があり、これにより複数の実行パスが発生し、VehicleRepository のテストが非常に複雑になります。
ビルダーの状態をテストするためにコードをリファクタリングします
。たとえば、getLuxuryCar 操作を 2 つの操作に分割します。
public Vehicle getLuxuryCar() {
VehicleBuilder builder = getLuxuryCarBuilder();
return builder.build();
}
// Visible for testing in the same package
protected VehicleBuilder getLuxuryCarBuilder() {
VehicleBuilder builder = getVehicleBuilder();
builder.setChassis(chassisDAO.findBySize(ChassisSize.NORMAL));
builder.setEngine(engineDAO.findByPower(EnginePower.HIGH));
builder.setWheelTemplate(wheelDAO.findBySize(WheelSize.NORMAL));
builder.addAccessory(accessoryDAO.findByType(Accessory.LUXURY));
return builder;
}
私は動作テストよりも状態テストを好みます。おそらくテストの方が弾力性がありますが、結果として得られるコード (特に、実際には何もしない getLuxuryCar() メソッド) は好きではありません。
VehicleBuilder をモックして動作
をテストする このような単体テストは、テストされる実際の操作のレプリカに過ぎず、実際の利点は何も追加されないと思います。また、非常に壊れやすく、テスト対象の操作の内部に完全に依存します。可能性のある単体テスト (JMock を使用) は次のようになります。
@Test
public void shouldBuildLuxuryCar() {
context.checking(new Expectations() {{
oneOf(chassisDAOmock).findBySize(ChassisSize.NORMAL);
oneOf(vehicleBuilderMock).setChassis(with(any(Chassis.class)));
oneOf(engineDAOmock).findByPower(EnginePower.HIGH);
oneOf(vehicleBuilderMock).setEngine(with(any(Engine.class)));
oneOf(wheelDAOmock).findBySize(WheelSize.NORMAL));
oneOf(vehicleBuilderMock).setWheelTemplate(with(any(Wheel.class)));
oneOf(accessoryDAOmock).findByType(Accessory.LUXURY));
oneOf(vehicleBuilderMock).setAccessories(with(any(Set.class)));
oneOf(vehicleBuilderMock).build();
}});
context.assertIsSatisfied();
}
単体テストを行わないでください
。代替手段のいずれにも満足できず、操作に実際にテストを必要としないため、このオプションに傾いています。明確にするために、VehicleBuilder の単体テストは引き続き行いますが、VehicleRepository の単体テストは行いません。ただし、たとえば次の質問でわかるように、これは一般的な意見ではないようです:単体テスト - テストしないもの