Autofixture: نهج لتوليد عينة عشوائية على أساس التخصيص

تم إنشاؤها على ٨ يناير ٢٠١٨  ·  11تعليقات  ·  مصدر: AutoFixture/AutoFixture

مرحبًا ، أريد أن أكون قادرًا على إنشاء قيم مميزة بناءً على ICustomization باستخدام ISpecimenBuilder.CreateMany . كنت أتساءل ما هو الحل الأفضل لأن AutoFixture سيولد نفس القيم لجميع الكيانات.

ج #
التخصيص Foo من الدرجة العامة: التخصيص
{
تخصيص الفراغ العام (IFixture fixture)
{
var عينة = تركيبات()
.OmitAutoProperties ()
مع (x => x.CreationDate، DateTime.Now)
. مع (x => x.Identifier، Guid.NewGuid (). ToString (). Substring (0، 6)) // يجب تكوين قيم مميزة
. مع (x => x.Mail، $ "[email protected]")
.إنشاء()؛

        fixture.Register(() => specimen);
}

}
""

ملاحظة: الهدف الرئيسي هو تقليل مقدار الشفرة في اختباراتي ، لذلك يجب أن أتجنب استدعاء With() لتخصيص القيم لكل اختبار. هل هناك طريقة مناسبة لفعل هذا؟

تحديث: لقد قرأت للتو هذا الرد من ploeh : https://github.com/AutoFixture/AutoFixture/issues/361#issuecomment -86873060 قد يكون هذا ما أبحث عنه.
هذا النهج له العديد من العيوب: أولاً يبدو أنه من غير المنطقي استدعاء Create<List<Foo>>() لأنه نوعًا ما يهزم الغرض من ما يمكن توقعه من CreateMany<Foo> ؛ التي من شأنها أن تولد قائمة بالحجم الثابت> (؟). عيب آخر هو أنه يجب أن يكون لدي تخصيصان لكل كيان ؛ واحد لإنشاء مجموعات مخصصة وآخر لإنشاء مثيل واحد ، نظرًا لأننا نتجاوز سلوك Create<T> لإنشاء مجموعات.

question

ال 11 كومينتر

يبدو هذا مرشحًا جيدًا لسؤال Stack Overflow . لا حرج في طرح سؤال هنا ، لكنني أعتقد أن هذا السؤال من النوع الذي قد تفيد فيه الإجابة الأشخاص الآخرين أيضًا ، ومن الأسهل العثور على أسئلة وإجابات على Stack Overflow مقارنة بمشكلات GitHub. أولاً ، يقوم Google بعمل جيد في فهرسة أسئلة Stack Overflow ، بينما يبدو أنه يصنف (مغلق) مشكلات GitHub أقل ...

ploeh أتفق معك ....
لقد قمت للتو بإنشاء سؤال SO بناءً على هذه المشكلة. هذا هو الرابط للمراجع المستقبلية 👍

ploeh شكرا للإجابة على السؤال على SO: أحمر الخدود:

thiagomajesk في الواقع لا توجد مشاكل في الإجابة على الأسئلة هنا. السبب وراء اقتراح مارك لـ SO هو إمكانات البحث التي يوفرها. يقوم مارك حاليًا أيضًا بمراقبة SO ، لذلك إذا قمت بنشر سؤال هناك ، فهناك المزيد من الفرص للحصول على خبرته (مثلما حصلت هذه المرة): sweat_smile:

zvirja شكرا لتوضيح ذلك 😄

zvirja سأغلق السؤال عن SO بناءً على إجابةploeh الرائعة.
لكني أود أن أطلب المزيد من المعلومات حول تفاصيل التنفيذ في هذا.
نظرًا لأنني لا أريد الاستطالة أكثر من الموضوع ، أعتقد أنه من الأفضل أن أسأل عن هذه التفاصيل هنا. لذلك لدي هذا التنفيذ:

ج #
فئة عامة UniqueShortGuidBuilder: ISpecimenBuilder
{
سلسلة خاصة للقراءة فقط propName ؛
للقراءة فقط في الطول ؛

public UniqueShortGuidBuilder(Expression<Func<TEntity, string>> expr, int lenght)
{
    propName = ((MemberExpression)expr.Body).Member.Name;
    this.lenght = lenght;
}

إنشاء كائن عام (طلب كائن ، سياق ISpecimenContext)
{
var pi = request as PropertyInfo ؛

if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
    return new NoSpecimen();

return Guid.NewGuid().ToString().Substring(0, lenght);

}
""

كنت أتوقع أنه عند إنشاء العديد من الكائنات باستخدام AutoFixture ، سيتم استدعاء ISpecimenBuilder.Create عدة مرات ، مما يؤدي إلى إنشاء أدلة مميزة للكيانات الخاصة بي ؛ لكن من الواضح أن هذا ليس صحيحًا وهم في الواقع يتشاركون نفس الشيء.

حسنًا ، لقد اختبرت الكود الخاص بك للتو وعملت بالضبط بالشكل المطلوب:

ج #
فئة عامة Foo
{
السلسلة العامة PropertyToCustomize {get؛ يضع؛ }
}

فئة عامة UniqueShortGuidBuilder: ISpecimenBuilder
{
سلسلة خاصة للقراءة فقط propName ؛
للقراءة فقط في الطول ؛

public UniqueShortGuidBuilder(Expression<Func<TEntity, string>> expr, int lenght)
{
    propName = ((MemberExpression) expr.Body).Member.Name;
    this.lenght = lenght;
}

public object Create(object request, ISpecimenContext context)
{
    var pi = request as PropertyInfo;

    if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
        return new NoSpecimen();

    return Guid.NewGuid().ToString().Substring(0, lenght);
}

}

[حقيقة]
اختبار الفراغ العام
{
var fixture = تركيبات جديدة () ؛
fixture.Customizations.Add (new UniqueShortGuidBuilder(x => x.PropertyToCustomize ، 5)) ؛

var manyFoos = fixture.CreateMany<Foo>(10);

var uniqueIds = manyFoos
    .Select(x => x.PropertyToCustomize)
    .Where(x => x.Length == 5)
    .Distinct()
    .ToArray();
Assert.Equal(10, uniqueIds.Length);

}
""

لذلك أود أن أسألك مرة أخرى كيف تختلف شفرتك الفعلية حتى نتمكن من فهم سبب أهمية هذا الاختلاف 😟

تضمين التغريدة الكود أدناه هو الحد الأدنى من إعادة سرد المشكلة التي أواجهها.
إذا قمت بإزالة Fixture.Customize(new FooCustomization()); فسوف يجتاز الاختبار ، وإلا فلن يجتاز الاختبار.

ج #
باستخدام النظام ؛
باستخدام Microsoft.VisualStudio.TestTools.UnitTesting ؛
باستخدام AutoFixture.Kernel ؛
باستخدام System.Linq.Expressions ؛
باستخدام System.Reflection.
باستخدام AutoFixture ؛
باستخدام System.Linq ؛

مساحة الاسم UnitTestProject1
{
فئة عامة Foo
{
السلسلة العامة PropertyToCustomize {get؛ يضع؛ }
}

public class FooCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        var specimen = fixture.Build<Foo>()
            .OmitAutoProperties()
            .With(x => x.PropertyToCustomize)
            .Create();

        fixture.Register(() => specimen);
    }
}

public class UniqueShortGuidBuilder<TEntity> : ISpecimenBuilder
{
    private readonly string propName;
    private readonly int lenght;

    public UniqueShortGuidBuilder(Expression<Func<TEntity, string>> expr, int lenght)
    {
        propName = ((MemberExpression)expr.Body).Member.Name;
        this.lenght = lenght;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as PropertyInfo;

        if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
            return new NoSpecimen();

        return Guid.NewGuid().ToString().Substring(0, lenght);
    }
}

[TestClass]
public class UnitTest1
{
    public static Fixture Fixture { get; private set; }

    [ClassInitialize]
    public static void ClassInitialize(TestContext context)
    {
        Fixture = new Fixture();
        Fixture.Customizations.Add(new UniqueShortGuidBuilder<Foo>(x => x.PropertyToCustomize, 5));
        Fixture.Customize(new FooCustomization());
    }

    [TestMethod]
    public void TestMethod1()
    {
        var manyFoos = Fixture.CreateMany<Foo>(10);

        var uniqueIds = manyFoos
            .Select(x => x.PropertyToCustomize)
            .Where(x => x.Length == 5)
            .Distinct()
            .ToArray();

        Assert.AreEqual(10, uniqueIds.Length);
    }
}

}
""

توصية: إذا كنت تعتمد على إزالة الأحرف _n_ الأولى من سلسلة GUID ، فلا تقم بتسمية الفئة UniqueShortGuidBuilder . قد تكون القيم فريدة إلى حد ما عندما يكون الطول 5 ، ولكن إذا قمت بتعيينه على 1 ، فمن المحتمل أن ترى تصادمات. على أي حال ، التفرد ليس مضمونًا.

إذا كنت تهتم فقط بطول السلسلة ، بالإضافة إلى التفرد غير المضمون ، فلماذا لا تسأل ببساطة عن رقم بين 0 و 99999 على سبيل المثال ، وتحويله إلى سلسلة؟ IIRC ، الأرقام فريدة حتى ينضب النطاق.

إذا قمت بإزالة Fixture.Customize (new FooCustomization ()) ؛ سوف ينجح الاختبار ، وإلا لا.

حسنًا ، في الواقع هذا هو السلوك المتوقع إذا راجعت الموقف بعناية أكبر 😅 يرجى الاطلاع على ردي في # 962. يؤدي التنفيذ المقدم لـ FooCustomization مثيل من النوع Foo وتكوين تركيبات لإرجاع هذا المثيل دائمًا. لذلك ، عندما تقوم لاحقًا بإنشاء مثيلات متعددة من النوع Foo ، في كل مرة يتم إرجاع نفس الكائن ولا يتم استدعاء المنشئ الخاص بك مرة أخرى.

إذا كنت تريد إنشاء مثيل جديد لكل مرة تطلب فيها Foo ، فاستخدم Customize<> API كما اقترحت هنا .

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

إذا كنت تهتم فقط بطول السلسلة ، بالإضافة إلى التفرد غير المضمون ، فلماذا لا تسأل ببساطة عن رقم بين 0 و 99999 على سبيل المثال ، وتحويله إلى سلسلة؟

أراهن أن هذا حل جيد لمعظم الحالات ، لكن لا يمكنني فعل ذلك لهذه الحالة المحددة بسبب عمليات التحقق من صحة الأبجدية الرقمية في نظامي. إلى جانب ذلك ، لدي عمليات تحقق أكثر تعقيدًا تتعلق بهذا المجال.

حسنًا ، في الواقع هذا هو السلوك المتوقع إذا راجعت الموقف بعناية أكبر

zvirja هذا واضح بعد ردك في # 962. لكن بالنسبة لي لم يكن هذا السلوك واضحًا ، نظرًا لعدم وجود وثائق (صريحة) مسبقة حول AutoFixture (كما أشرت هنا ) 😁.

في الختام ، أشكركم على الصبر وجميع التفسيرات الممتازة.
أنا متأكد من أن هذا سيكون مفيدًا للآخرين في المستقبل أيضًا.

حسنًا ، عادل بما يكفي ، قد أميل إلى اعتبار الأشياء حرفية جدًا 😄

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

tiesmaster picture tiesmaster  ·  7تعليقات

josh-degraw picture josh-degraw  ·  4تعليقات

zvirja picture zvirja  ·  3تعليقات

Ephasme picture Ephasme  ·  3تعليقات

Accc99 picture Accc99  ·  4تعليقات