سؤال

منذ فترة قرأت يسخر ليست بذرة مقال بقلم مارتن فاولر ويجب أن أعترف بأنني خائف قليلاً من التبعيات الخارجية فيما يتعلق بالتعقيد الإضافي لذا أود أن أسأل:

ما هي أفضل طريقة لاستخدامها عند اختبار الوحدة؟

هل من الأفضل دائمًا استخدام إطار عمل وهمي للسخرية تلقائيًا من تبعيات الطريقة التي يتم اختبارها، أم هل تفضل استخدام آليات أبسط مثل بذرة الاختبار على سبيل المثال؟

هل كانت مفيدة؟

المحلول

كما يقول الشعار "اذهب مع أبسط شيء يمكن أن ينجح".

  1. إذا كانت الفصول المزيفة قادرة على إنجاز المهمة، فاتبعها.
  2. إذا كنت تريد واجهة ذات طرق متعددة ليتم الاستهزاء بها، فانتقل إلى إطار عمل وهمي.

تجنب استخدام المحاكاة دائماً لأنها تجعل الاختبارات هشة.تتمتع اختباراتك الآن بمعرفة معقدة بالطرق التي يستدعيها التنفيذ، إذا تغيرت الواجهة الساخرة أو التنفيذ الخاص بك...كسر اختباراتك.يعد هذا أمرًا سيئًا لأنك ستقضي وقتًا إضافيًا في تشغيل اختباراتك بدلاً من تشغيل SUT فقط. لا ينبغي أن تكون الاختبارات حميمة بشكل غير مناسب مع التنفيذ.
لذلك استخدم حكمك الأفضل..أفضّل المحاكاة عندما يساعدني ذلك في إنقاذي من الكتابة وتحديث فصل دراسي مزيف باستخدام n >> 3 طرق.

تحديث الخاتمة/المداولة:
(شكرًا لـ Toran Billups على سبيل المثال للاختبار الساخر.انظر أدناه)
مرحبًا دوج، حسنًا، أعتقد أننا انتقلنا إلى حرب مقدسة أخرى - Classic TDDers vs Mockist TDDers.أعتقد أنني أنتمي إلى السابق.

  • إذا كنت في الاختبار رقم 101 Test_ExportProductList ووجدت أنني بحاجة إلى إضافة معلمة جديدة إلى IProductService.GetProducts().أفعل ذلك للحصول على هذا الاختبار باللون الأخضر.أستخدم أداة إعادة البناء لتحديث جميع المراجع الأخرى.والآن أجد كل الاختبارات الساخرة التي تنادي بهذا العضو تنفجر الآن.ثم لا بد لي من العودة وتحديث كل هذه الاختبارات - مضيعة للوقت.لماذا فشل يجب PopulateProductsListOnViewLoadWhenPostBackIsFalse؟هل كان ذلك بسبب كسر الكود؟بل الاختبارات مكسورة.أنا أؤيد فشل اختبار واحد = مكان واحد لإصلاحه.التكرار الساخر يتعارض مع ذلك.هل ستكون الركائز أفضل؟إذا كان لدي fake_class.GetProducts()..تأكد من مكان واحد للتغيير بدلاً من جراحة البندقية عبر مكالمات توقع متعددة.في النهاية المسألة مسألة أسلوب..إذا كان لديك طريقة فائدة شائعة MockHelper.SetupExpectForGetProducts() - فهذا يكفي أيضًا..ولكن سترى أن هذا غير شائع.
  • إذا قمت بوضع شريط أبيض على اسم الاختبار، فسيكون من الصعب قراءة الاختبار.يخفي الكثير من أكواد السباكة الخاصة بالإطار الوهمي الاختبار الفعلي الذي يتم إجراؤه.
  • يتطلب منك أن تتعلم هذه النكهة الخاصة لإطار العمل الساخر

نصائح أخرى

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

في بعض الأحيان، لا تريد توقعات بشأن كل طريقة تستدعيها، لذلك ستسمح بعض أطر العمل الساخرة بالنماذج "الجزئية" التي تشبه الهجين الوهمي/الكعب، حيث يتم فرض التوقعات التي تحددها فقط، ويتم التعامل مع كل استدعاء أسلوب آخر أشبه بكعب الروتين لأنه يُرجع قيمة فقط.

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

وإلى هذا...

تجنب استخدام المحاكاة دائمًا لأنها تجعل الاختبارات هشة.تتمتع اختباراتك الآن بمعرفة معقدة بالطرق التي يستدعيها التنفيذ، إذا تغيرت الواجهة الساخرة...كسر اختباراتك.لذا استخدم أفضل حكم لديك..<

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

من الأفضل استخدام مجموعة، وسيتعين عليك استخدام حكمك الخاص.إليك الإرشادات التي أستخدمها:

  • إذا كان استدعاء التعليمات البرمجية الخارجية جزءًا من السلوك المتوقع (المواجه للخارج) للتعليمات البرمجية الخاصة بك، فيجب اختبار ذلك.استخدم وهمية.
  • إذا كانت المكالمة عبارة عن تفاصيل تنفيذ لا يهتم بها العالم الخارجي، فافضل بذرة.لكن:
  • إذا كنت قلقًا من أن عمليات التنفيذ اللاحقة للتعليمات البرمجية التي تم اختبارها قد تلتف حول جذورك عن طريق الخطأ، وتريد ملاحظة ما إذا حدث ذلك، فاستخدم نموذجًا وهميًا.أنت تقوم بربط الاختبار الخاص بك بالرمز الخاص بك، ولكن من أجل ملاحظة أن كعب الروتين الخاص بك لم يعد كافيًا وأن الاختبار الخاص بك يحتاج إلى إعادة العمل.

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

ومرة أخرى، هذه رائحة الكود والملاذ الأخير.إذا وجدت أنك بحاجة إلى القيام بذلك كثيرًا، فحاول إعادة التفكير في الطريقة التي تكتب بها اختباراتك.

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

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

<TestMethod()> _
Public Sub Should_Populate_Products_List_OnViewLoad_When_PostBack_Is_False()
    mMockery = New MockRepository()
    mView = DirectCast(mMockery.Stub(Of IProductView)(), IProductView)
    mProductService = DirectCast(mMockery.DynamicMock(Of IProductService)(), IProductService)
    mPresenter = New ProductPresenter(mView, mProductService)
    Dim ProductList As New List(Of Product)()
    ProductList.Add(New Product())
    Using mMockery.Record()
        SetupResult.For(mView.PageIsPostBack).Return(False)
        Expect.Call(mProductService.GetProducts()).Return(ProductList).Repeat.Once()
    End Using
    Using mMockery.Playback()
        mPresenter.OnViewLoad()
    End Using
    'Verify that we hit the service dependency during the method when postback is false
    Assert.AreEqual(1, mView.Products.Count)
    mMockery.VerifyAll()
End Sub

لا يهم الدولة مقابل.تفاعل.فكر في الأدوار والعلاقات.إذا تعاون كائن مع أحد الجيران لإنجاز مهمته، فإن هذه العلاقة (كما يتم التعبير عنها في الواجهة) تكون مرشحة للاختبار باستخدام النماذج.إذا كان الكائن عبارة عن كائن ذو قيمة بسيطة مع القليل من السلوك، فاختبره مباشرة.لا أستطيع أن أرى الهدف من كتابة السخرية (أو حتى بذرة) باليد.هذه هي الطريقة التي بدأنا بها جميعًا وأعدنا هيكلتها بعيدًا عن ذلك.

لإجراء مناقشة أطول، فكر في إلقاء نظرة على http://www.mockobjects.com/book

اقرأ مناقشة Luke Kanies حول هذا السؤال بالضبط في هذا بلوق وظيفة.يشير مشاركة من جاي فيلدز مما يشير أيضًا إلى أن استخدام [ما يعادل Ruby's/mocha's] stub_everything هو الأفضل لجعل الاختبارات أكثر قوة.لاقتباس كلمات فيلدز الأخيرة:"يجعل Mocha من السهل تعريف المحاكاة كما هو الحال مع تعريف كعب الروتين، ولكن هذا لا يعني أنه يجب عليك دائمًا تفضيل المحاكاة.في الواقع، أفضّل عمومًا البذرة وأستخدم المحاكاة عند الضرورة."

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top