سؤال

تحتوي الامتدادات التفاعلية على خطاف صغير مثير لتبسيط استدعاء طرق ASYNC:

var func = Observable.FromAsyncPattern<InType, OutType>(
    myWcfService.BeginDoStuff,
    myWcfService.EndDoStuff);

func(inData).ObserveOnDispatcher().Subscribe(x => Foo(x));

أنا أستخدم هذا في مشروع WPF ، وهو يعمل بشكل رائع في وقت التشغيل.

لسوء الحظ ، عند محاولة وحدة الاختبار التي تستخدم هذه التقنية ، أواجه إخفاقات عشوائية. ~ 3 من كل خمسة عمليات إعدام للاختبار الذي يحتوي على هذا الرمز فشل.

فيما يلي اختبار عينة (تم تنفيذه باستخدام حاوية وحاوية وحدات وحدات التلقائية):

[TestMethod()]
public void SomeTest()
{
   // arrange
   var container = GetAutoMockingContainer();

   container.Resolve<IMyWcfServiceClient>()
      .Expect(x => x.BeginDoStuff(null, null, null))
      .IgnoreArguments()
      .Do(
         new Func<Specification, AsyncCallback, object, IAsyncResult>((inData, asyncCallback, state) =>
            {
               return new CompletedAsyncResult(asyncCallback, state);
             }));

   container.Resolve<IRepositoryServiceClient>()
      .Expect(x => x.EndDoStuff(null))
      .IgnoreArguments()
      .Do(
         new Func<IAsyncResult, OutData>((ar) =>
         {
            return someMockData;
         }));

   // act
   var target = CreateTestSubject(container);

   target.DoMethodThatInvokesService();

   // Run the dispatcher for everything over background priority
   Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new Action(() => { }));

   // assert
   Assert.IsTrue(my operation ran as expected);
}

المشكلة التي أراها هي أن الكود الذي حددته لتشغيله عند الانتهاء من إجراء ASYNC (في هذه الحالة ، FOO (X)) ، لا يسمى أبدًا. يمكنني التحقق من ذلك عن طريق تعيين نقاط التوقف في FOO ومراقبة أنه لم يتم الوصول إليها مطلقًا. علاوة على ذلك ، يمكنني فرض تأخير طويل بعد استدعاء DomethodthatinVokEstervice (الذي ينطلق مكالمة Async) ، ولا يزال الرمز يعمل أبدًا. أنا فعل اعلم أن خطوط الكود التي تستدعي إطار RX تم استدعاؤها.

أشياء أخرى جربتها:

هذا تحسين معدل الفشل إلى شيء مثل 1 من كل 5 ، لكنهم ما زالوا قد حدثوا.

  • لقد قمت بإعادة كتابة رمز RX لاستخدام نمط Jane Async العادي. هذا يعمل ، ومع ذلك ، فإن الأنا المطور الخاص بي يحب استخدام RX بدلاً من البداية/النهاية القديمة الممل.

في النهاية ، لدي عمل في متناول اليد (أي لا تستخدم RX) ، لكنني أشعر أنه ليس مثاليًا. إذا واجه أي شخص هذه المشكلة في الماضي ووجد حلاً ، فأنا أحب أن أسمعها بشدة.

تحديث:

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

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

المحلول

المشكلة ناتجة عن الطبيعة غير المتزامنة للمكالمات المجدولة ObserveOnDispatcher. لا يمكنك ضمان اكتمال جميعها بحلول الوقت الذي ينتهي فيه الاختبار. لذلك تحتاج إلى جعل الجدولة تحت سيطرتك.

ماذا عن حقن الجدولة في صفك؟

ثم ، بدلاً من الاتصال ObserveOnDispatcher, ، أنت أتصل ObserveOn, ، يمر في IScheduler التنفيذ الذي تم حقنه.

في وقت التشغيل ، ستقوم بحقن DispatcherScheduler, ، ولكن في اختباراتك ، ستقوم بضخ جدولة مزيفة تقوم بتصوير جميع الإجراءات التي يتم إعطاؤها وتشغيلها في وقت يتم التحكم فيه بواسطة اختباراتك.

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

public static MyObservableExtensions
{
   public static IScheduler UISafeScheduler {get;set;}

   public static IObservable<TSource> ObserveOnUISafeScheduler(this IObservable<TSource> source)
   {
       if (UISafeScheduler == null) 
       {
          throw new InvalidOperation("UISafeScheduler has not been initialised");
       }

       return source.ObserveOn(UISafeScheduler);
   }
}

ثم في وقت التشغيل ، initionise Uisafescheduler مع Dispatcherscheduler ، وفي اختباراتك ، ارفعها مع جدولة المزيفة الخاصة بك.

نصائح أخرى

المشكلة هي أن mStest.exe يدير مرسلًا (أي مرسل. ومع ذلك ، هذا المرسل لا يفعل شيئا! (أي سيتم تجاهل عناصر المرسل في قائمة الانتظار) أي رمز تكتبه يستخدم بشكل صريح الجدول الزمني. dispatcher

لقد حلت هذا عن طريق القوة الغاشمة في Reactiveui - ها هي البتات المهمة:

https://github.com/reactiveui/Reactiveui/Blob/master/reactiveui/Rxapp.cs#l99

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

بعد ذلك ، في فصولنا التي تنفذ iobservable ، تأخذ جميعها معلمة iScheduler ، والتي ستنتهي قيمتها الافتراضية هي الجدولة الافتراضية العالمية. ربما كان بإمكاني القيام بعمل أفضل ، لكن هذا يناسبني ويجعل رمز ViewModel قابل للاختبار مرة أخرى.

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