مرحبًا ، أريد أن أكون قادرًا على إنشاء قيم مميزة بناءً على 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>
لإنشاء مجموعات.
يبدو هذا مرشحًا جيدًا لسؤال Stack Overflow . لا حرج في طرح سؤال هنا ، لكنني أعتقد أن هذا السؤال من النوع الذي قد تفيد فيه الإجابة الأشخاص الآخرين أيضًا ، ومن الأسهل العثور على أسئلة وإجابات على Stack Overflow مقارنة بمشكلات GitHub. أولاً ، يقوم Google بعمل جيد في فهرسة أسئلة Stack Overflow ، بينما يبدو أنه يصنف (مغلق) مشكلات GitHub أقل ...
ploeh أتفق معك ....
لقد قمت للتو بإنشاء سؤال SO بناءً على هذه المشكلة. هذا هو الرابط للمراجع المستقبلية 👍
ploeh شكرا للإجابة على السؤال على SO: أحمر الخدود:
thiagomajesk في الواقع لا توجد مشاكل في الإجابة على الأسئلة هنا. السبب وراء اقتراح مارك لـ SO هو إمكانات البحث التي يوفرها. يقوم مارك حاليًا أيضًا بمراقبة SO ، لذلك إذا قمت بنشر سؤال هناك ، فهناك المزيد من الفرص للحصول على خبرته (مثلما حصلت هذه المرة): sweat_smile:
zvirja شكرا لتوضيح ذلك 😄
zvirja سأغلق السؤال عن SO بناءً على إجابةploeh الرائعة.
لكني أود أن أطلب المزيد من المعلومات حول تفاصيل التنفيذ في هذا.
نظرًا لأنني لا أريد الاستطالة أكثر من الموضوع ، أعتقد أنه من الأفضل أن أسأل عن هذه التفاصيل هنا. لذلك لدي هذا التنفيذ:
ج #
فئة عامة UniqueShortGuidBuilder
{
سلسلة خاصة للقراءة فقط 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
{
سلسلة خاصة للقراءة فقط 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
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 (كما أشرت هنا ) 😁.
في الختام ، أشكركم على الصبر وجميع التفسيرات الممتازة.
أنا متأكد من أن هذا سيكون مفيدًا للآخرين في المستقبل أيضًا.
حسنًا ، عادل بما يكفي ، قد أميل إلى اعتبار الأشياء حرفية جدًا 😄