AutoFixtureリファクタリング
-
26-09-2019 - |
質問
私はユニットテストが肥大化したようAutoFixture http://autofixture.codeplex.com/ に使用し始めデータ・セットアップの多くの。私は私のユニットテストを書くことよりも、データをアップSETINGに多くの時間を費やしました。ここで(DDDブルーブックからの貨物のアプリケーションのサンプルから取られた例)
のようにどのように私の最初のユニットテストルックスの例を示します。[Test]
public void should_create_instance_with_correct_ctor_parameters()
{
var carrierMovements = new List<CarrierMovement>();
var deparureUnLocode1 = new UnLocode("AB44D");
var departureLocation1 = new Location(deparureUnLocode1, "HAMBOURG");
var arrivalUnLocode1 = new UnLocode("XX44D");
var arrivalLocation1 = new Location(arrivalUnLocode1, "TUNIS");
var departureDate1 = new DateTime(2010, 3, 15);
var arrivalDate1 = new DateTime(2010, 5, 12);
var carrierMovement1 = new CarrierMovement(departureLocation1, arrivalLocation1, departureDate1, arrivalDate1);
var deparureUnLocode2 = new UnLocode("CXRET");
var departureLocation2 = new Location(deparureUnLocode2, "GDANSK");
var arrivalUnLocode2 = new UnLocode("ZEZD4");
var arrivalLocation2 = new Location(arrivalUnLocode2, "LE HAVRE");
var departureDate2 = new DateTime(2010, 3, 18);
var arrivalDate2 = new DateTime(2010, 3, 31);
var carrierMovement2 = new CarrierMovement(departureLocation2, arrivalLocation2, departureDate2, arrivalDate2);
carrierMovements.Add(carrierMovement1);
carrierMovements.Add(carrierMovement2);
new Schedule(carrierMovements).ShouldNotBeNull();
}
ここで私はAutoFixtureでそれをリファクタリングしようとした方法です。
[Test]
public void should_create_instance_with_correct_ctor_parameters_AutoFixture()
{
var fixture = new Fixture();
fixture.Register(() => new UnLocode(UnLocodeString()));
var departureLoc = fixture.CreateAnonymous<Location>();
var arrivalLoc = fixture.CreateAnonymous<Location>();
var departureDateTime = fixture.CreateAnonymous<DateTime>();
var arrivalDateTime = fixture.CreateAnonymous<DateTime>();
fixture.Register<Location, Location, DateTime, DateTime, CarrierMovement>(
(departure, arrival, departureTime, arrivalTime) => new CarrierMovement(departureLoc, arrivalLoc, departureDateTime, arrivalDateTime));
var carrierMovements = fixture.CreateMany<CarrierMovement>(50).ToList();
fixture.Register<List<CarrierMovement>, Schedule>((carrierM) => new Schedule(carrierMovements));
var schedule = fixture.CreateAnonymous<Schedule>();
schedule.ShouldNotBeNull();
}
private static string UnLocodeString()
{
var stringBuilder = new StringBuilder();
for (int i = 0; i < 5; i++)
stringBuilder.Append(GetRandomUpperCaseCharacter(i));
return stringBuilder.ToString();
}
private static char GetRandomUpperCaseCharacter(int seed)
{
return ((char)((short)'A' + new Random(seed).Next(26)));
}
私はそれをリファクタリングするより良い方法がありますかどうかを知りたいと思います。それよりも、それは短く、簡単にしたいと思っています。
解決
あなたの初期の試みはよさそうだが、物事の少なくともカップルは、あなたが少しを簡素化することができますがあります。
まず第一に、あなたはこれを削減することができる必要があります:
fixture.Register<Location, Location, DateTime, DateTime, CarrierMovement>(
(departure, arrival, departureTime, arrivalTime) =>
new CarrierMovement(departureLoc, arrivalLoc, departureDateTime, arrivalDateTime));
これを
fixture.Register<Location, Location, DateTime, DateTime, CarrierMovement>(
() => new CarrierMovement(departureLoc, arrivalLoc, departureDateTime, arrivalDateTime));
あなたはそれらの他の変数を使用していないからです。しかし、これは本質的に同じ四つの値を使用するCarrierMovementのいずれかの作成をロックします。各作成CarrierMovementが別々のインスタンスになりますが、それらはすべての共有と同じ4つの値が、私はそれはあなたが何を意味するかだったかしらでしょう?
同じ静脈に上記のように、代わりの
fixture.Register<List<CarrierMovement>, Schedule>((carrierM) =>
new Schedule(carrierMovements));
あなたが書くことができます。
fixture.Register(() => new Schedule(carrierMovements));
あなたはcarrierM
変数を使用していないので。型推論はあなたが原因のFuncの戻り値の型のスケジュールを登録していることを把握します。
しかし、想定している。このようなスケジュールコンストラクタルックスます:
public Schedule(IEnumerable<CarrierMovement> carrierMovements)
あなただけではなく、このようcarrierMovements
を登録している可能性があります:
fixture.Register<IEnumerable<CarrierMovement>>(carrierMovements);
AutoFixtureが自動的に正しくスケジュールを解決する原因となります。それは(AutoFixtureは、パラメータの型を解決できる限り)あなたがテストを壊すことなく、将来的にスケジュールのコンストラクタにパラメータを追加することができますので、このアプローチは、より保守である。
私たちが本当に登録よりも何かのためにcarrierMovements
変数を使用していないので、しかし、我々はこのような場合には、より良いものより行うことができます。私たちが本当に行う必要があることだけIEnumerable<CarrierMovement>
のインスタンスを作成する方法をAutoFixtureを伝えることです。あなたは(あなたがいけない)数50気にしないのであれば、私たちも、このようなメソッドグループの構文を使用することができます:
fixture.Register(fixture.CreateMany<CarrierMovement>);
お知らせメソッド呼び出しの括弧の欠如:。我々はのFuncを登録している、とCreateMany<T>
メソッドが返すのIEnumerable<T>
タイプので、推論は、残りの世話をする。
しかし、それらはすべての詳細です。より高いレベルでは、あなたはすべてのCarrierMovementを登録していない検討する必要があります。このコンストラクタを仮定します:
public CarrierMovement(Location departureLocation,
Location arrivalLocation,
DateTime departureTime,
DateTime arrivalTime)
autofixture自身でそれを把握することができる必要があります。
それはすべてのdepartureLocationとarrivalLocationのための新しい場所のインスタンスを作成しますが、それはあなたが手動で元のテストで何をしたかよりも異なるません。
それは到着時刻が出発時刻前になることはありませんことを、少なくとも性を保証デフォルトAutoFixtureの用途のDateTime.Now
、によって、倍になります。しかし、彼らが同一である可能性が非常に高いが、それが問題であるならば、あなたは常に自動インクリメント機能を登録することができます。
これらの配慮を考えると、ここでの代替があります:
public void should_create_instance_with_correct_ctor_parameters_AutoFixture()
{
var fixture = new Fixture();
fixture.Register(() => new UnLocode(UnLocodeString()));
fixture.Register(fixture.CreateMany<CarrierMovement>);
var schedule = fixture.CreateAnonymous<Schedule>();
schedule.ShouldNotBeNull();
}
<時間>
あなたがそれを登録する必要がありますIList<CarrierMovement>
で問題を解決するには。ここではそれを行うための一つの方法だ。
fixture.Register<IList<CarrierMovement>>(() =>
fixture.CreateMany<CarrierMovement>().ToList());
あなたが聞いているので、しかし、私はこのようなスケジュールコンストラクタルックスことを意味します:
public Schedule(IList<CarrierMovement> carrierMovements)
と私は本当にあなたがIEnumerable<Carriemovement>
を取るためにそのAPIを変更再考すべきだと思います。 APIの設計の観点から、(コンストラクタを含む)任意の部材を介して収集を供給すること(これの追加、削除およびクリアメソッドを呼び出すことによって、例えば)メンバーは、コレクションを変更することが許可されていることを意味します。それはほとんどあなたがコンストラクタから期待行動ではありませんので、それを許可していません。
AutoFixtureは自動的に私の上記の例では、すべてのLocation
オブジェクトの新しい値を生成しますが、CPUの速度に起因するだろう、日時の後続のインスタンスは、おそらく同一であるとされます。
、あなたは増加のDateTimeにそれが呼び出されますたびに返されたことを、小さなクラスを記述することができます。私が興味のある読者にそのクラスの実装を残しておきますが、あなたはそのようにようにそれを登録することができます:
var dtg = new DateTimeGenerator();
fixture.Register(dtg.Next);
(上記よりメソッドグループの構文一度告知)このAPIを想定します:
public class DateTimeGenerator
{
public DateTime Next();
}