عندما يبدأ الاختبار من خلال الطريقة العامة بالرائحة الكريهة (مثال)

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

مثال على الأسهم


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

كيف ستقوم بتطبيق واختبار العلم؟

using System; using System.Threading.Tasks; using System.Threading; using System.Messaging; namespace PublicMethodNottrivialSample { public class MessageProcessorService { private object _lockObject = new object(); private CancellationTokenSource _cancellationSource; private Action _receiveMessage; private int _listenerThreads = 5; public void Start() { lock (_lockObject) { _cancellationSource = new CancellationTokenSource(); Task.Factory.StartNew(() => InitializeReceiveLoop(_cancellationSource.Token), _cancellationSource.Token); } } private void InitializeReceiveLoop(CancellationToken cancellationToken) { _receiveMessage = () => { while (!cancellationToken.IsCancellationRequested) { using (MessageQueueTransaction msgTx = new MessageQueueTransaction()) { try { msgTx.Begin(); //   MessageQueue queue = new MessageQueue(); Message message = queue.Receive(msgTx); //    ,    if (!cancellationToken.IsCancellationRequested) { ProcessMessage(message); } msgTx.Commit(); } catch (Exception ex) { // some logging } } } }; for (int n = 0; n < _listenerThreads; n++) { StartProcessingLoop(cancellationToken); } } private void ProcessMessage(Message message) { //   ,     } private void StartProcessingLoop(CancellationToken cancellationToken) { Task.Factory.StartNew(_receiveMessage, cancellationToken); } } } 

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

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

بشكل عام ، من الضروري اختبار مثل هذا الرمز ، ولكن ليس مع اختبارات الوحدة ، ولكن مع اختبارات التكامل.

"أنت الداما ، أو اذهب"


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

  1. يمكن اختبار الطريقة الخاصة
  2. يمكن نشر هذه الطريقة للجمهور
  3. يمكن أن تكون داخلية
  4. يمكن سحبها إلى فصل منفصل كطريقة عامة

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

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

يمكن جعل الطرق الداخلية مرئية لتجميع الاختبار باستخدام السمة InternalsVisibleTo واستدعاؤها كالمعتاد. هذا النهج يجعل اختبارات الكتابة أسهل ، وستكون النتيجة هي نفسها.

اختبار عملية الرسالة


دعنا نعود إلى المهمة. باتباع النهج أعلاه ، سأقوم ببعض التغييرات:

  • سيجعل ProcessMessage عامًا
  • ستنشئ طريقة داخلية محمية جديدة لمنطق العلم (مثل GetFlagValue)
  • سيكتب اختبارات GetFlagValue لجميع الحالات
  • أود أن أكتب اختبارًا لـ ProcessMessage للتأكد من استخدام GetFlagValue بشكل صحيح: يتم تمرير المعلمات بشكل صحيح ويتم استخدامها حقًا

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

  • يتم تغطية جميع الحالات من خلال اختبارات وحدة GetFlagValue
  • اختبارات وحدة ProcessMessage تتحقق من استخدام أسلوب العلامة بشكل صحيح

أعتقد أنه وفقًا لذلك ، يمكنك كتابة اختبار وحدة واحد فقط لـ ProcessMessage وعدة اختبارات أخرى لـ GetFlagValue.

شيء من هذا القبيل. آرائكم

Source: https://habr.com/ru/post/ar420567/


All Articles