سؤال

بدأت في استخدام AutoFixture http://autofixture.codeplex.com/ نظرًا لأن اختبارات الوحدة الخاصة بي قد انتفخت مع الكثير من إعداد البيانات. كنت أقضي وقتًا أطول في إعداد البيانات بدلاً من كتابة اختبار الوحدة الخاص بي. إليك مثال على كيف يبدو اختبار الوحدة الأولي (مثال مأخوذ من عينة تطبيق الشحن من DDD Blue Book)

[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 سيكون مثيلًا منفصلاً ، إلا أنه سيشاركهم جميعًا نفس القيم الأربع ، وأتساءل عما إذا كان هذا هو ما قصدته؟

في نفس السياق على النحو الوارد أعلاه ، بدلاً من

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 يمكنه حل نوع المعلمة).

ومع ذلك ، يمكننا أن نفعل أفضل من ذلك في هذه الحالة لأننا لا نستخدم حقًا carrierMovements متغير لأي شيء آخر غير التسجيل. ما يتعين علينا فعله حقًا هو فقط معرفة AutoFixture كيفية إنشاء مثيلات من IEnumerable<CarrierMovement>. إذا كنت لا تهتم بالرقم 50 (لا ينبغي عليك) ، فيمكننا حتى استخدام بناء جملة Method مثل هذا:

fixture.Register(fixture.CreateMany<CarrierMovement>);

لاحظ الافتقار إلى Parantheses الاحتجاجية: نحن نسجل FUNC ، ومنذ ذلك الحين CreateMany<T> طريقة العودة IEnumerable<T> اكتب الاستدلال يعتني بالباقي.

ومع ذلك ، هذه كلها تفاصيل. على مستوى أعلى ، قد ترغب في التفكير في عدم تسجيل carriermovement على الإطلاق. على افتراض هذا المُنشئ:

public CarrierMovement(Location departureLocation,
    Location arrivalLocation,
    DateTime departureTime,
    DateTime arrivalTime)

يجب أن يكون Autofixture قادرًا على معرفة ذلك بنفسه.

سيؤدي ذلك إلى إنشاء مثيل موقع جديد لكل حلول مغادرة وإجراءات توضيح ، لكن هذا لا يختلف عن ما قمت به يدويًا في الاختبار الأصلي.

عندما يتعلق الأمر بالأوقات ، فاستخدمات 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 ، فإن توفير مجموعة من خلال أي عضو (بما في ذلك مُنشئ) يعني أنه يُسمح للعضو بتعديل المجموعة (على سبيل المثال من خلال استدعاء الأساليب والإزالة والإزالة). هذا بالكاد سلوك تتوقعه من مُنشئ ، لذلك لا تسمح بذلك.

سيقوم AutoFixture تلقائيًا بإنشاء قيم جديدة للجميع Location الكائنات في مثال بلدي أعلاه ، ولكن بسبب سرعة وحدة المعالجة المركزية ، من المحتمل أن تكون مثيلات DateTime اللاحقة متطابقة.

إذا كنت تريد زيادة أخصائيات البيانات ، فيمكنك كتابة فصل صغير يزيد من وقت البيانات الذي تم إرجاعه في كل مرة يتم فيها الاحتجاج بها. سأترك تنفيذ تلك الفئة للقارئ المهتمة ، ولكن يمكنك تسجيله بعد ذلك:

var dtg = new DateTimeGenerator();
fixture.Register(dtg.Next);

بافتراض أن واجهة برمجة التطبيقات هذه (لاحظ مرة أخرى بناء جملة مجموعة الأسلوب أعلاه):

public class DateTimeGenerator
{
    public DateTime Next();
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top