5

私の質問は、単純化された例(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 の単体テストは行いません。ただし、たとえば次の質問でわかるように、これは一般的な意見ではないようです:単体テスト - テストしないもの

4

1 に答える 1

3

あなたのポイントはすべてメリットがあると思いますが、これは私がやろうとしていることの単なる推奨です。

統合テストは、モックデータを生成するかテストデータソースを持つ注入されたDAOオブジェクトを使用して、このコードに適しています。

ただし、あなたの質問は単体テストに固有のものです。その場合、(ビルダーをあざけることなく)返却された車両オブジェクトに対してテストすることが最良のオプションだと思います。複雑さを避けるために、車両のタイプに関する詳細(このテストは車両の製造者/生産者に任されています)ではなく、返却された車両の単純なテスト(nullではなく、おそらくそのクラスの不変条件を確認してください)を続けます。

テストのこの利点は次のとおりです。

  1. 車両/ビルダーが後で追加のプロパティを必要とするように拡張されたが(たとえば、すべての車両にブレーキが必要)、開発者がリポジトリの更新を怠った場合、テストは失敗します。
  2. これは、少なくともコードが例外なく実行されることを保証する健全性テストとして機能します。
于 2012-10-13T14:39:27.040 に答える