كيف يمكنني اختبار اشتراكات مجمع الأحداث Prism ، على uithread؟

StackOverflow https://stackoverflow.com/questions/2375178

سؤال

لدي فصل ، يشترك في حدث عبر مجمع الأحداث Prisms.

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

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

المشكلة هي أنه خلال الاختبار ، لم يتم ملاحظة الحدث أبدًا في النظام قيد الاختبار إلا إذا لم أشترك في Uithread.

أنا أستخدم MSPEC للاختبارات الخاصة بي ، والتي أركضها من الداخل VS2008 عبر TDD.NET. مضيفا [RequiresSta] إلى فصل الاختبار الخاص بي لم يساعد

هل لدى أي شخص حل ، يوفرني من تغيير الخيوط أثناء اختباراتي (على سبيل المثال عبر خاصية - يا له من اختراق قبيح) ؟؟؟

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

المحلول

إذا كنت تسخر من كل من الحدث ومجمع الحدث ، واستخدمت رد اتصال MOQ ، فيمكنك القيام بذلك.

هذا مثال:

Mock<IEventAggregator> mockEventAggregator;
Mock<MyEvent> mockEvent;

mockEventAggregator.Setup(e => e.GetEvent<MyEvent>()).Returns(mockEvent.Object);

// Get a copy of the callback so we can "Publish" the data
Action<MyEventArgs> callback = null;

mockEvent.Setup(
    p =>
    p.Subscribe(
        It.IsAny<Action<MyEventArgs>>(), 
        It.IsAny<ThreadOption>(), 
        It.IsAny<bool>(), 
        It.IsAny<Predicate<MyEventArgs>>()))
        .Callback<Action<MyEventArgs>, ThreadOption, bool, Predicate<MyEventArgs>>(
        (e, t, b, a) => callback = e);


// Do what you need to do to get it to subscribe

// Callback should now contain the callback to your event handler
// Which will allow you to invoke the callback on the test's thread
// instead of the UI thread
callback.Invoke(new MyEventArgs(someObject));

// Assert

نصائح أخرى

أعتقد حقًا أنه يجب عليك استخدام Mocks لكل شيء وليس eventaggregator. ليس من الصعب السخرية على الإطلاق ... لا أعتقد أن الإجابة المرتبطة تثبت الكثير من أي شيء عن قابلية اختبار Eventaggregator.

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

//Arrange!
Mock<IEventAggregator> eventAggregatorMock = new Mock<IEventAggregator>();
Mock<PlantTreeNodeSelectedEvent> eventBeingListenedTo = new Mock<PlantTreeNodeSelectedEvent>();

Action<int> theActionPassed = null;
//When the Subscribe method is called, we are taking the passed in value
//And saving it to the local variable theActionPassed so we can call it.
eventBeingListenedTo.Setup(theEvent => theEvent.Subscribe(It.IsAny<Action<int>>()))
                    .Callback<Action<int>>(action => theActionPassed = action);

eventAggregatorMock.Setup(e => e.GetEvent<PlantTreeNodeSelectedEvent>())
                   .Returns(eventBeingListenedTo.Object);

//Initialize the controller to be tested.
PlantTreeController controllerToTest = new PlantTreeController(eventAggregatorMock.Object);

//Act!
theActionPassed(3);

//Assert!
Assert.IsTrue(controllerToTest.MyValue == 3);

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

أولاً ، قمت بإنشاء طريقة تمديد مثل ذلك:

public static class ThreadingExtensions
{
    private static ThreadOption? _uiOverride;

    public static ThreadOption UiOverride
    {
        set { _uiOverride = value; }
    }

    public static ThreadOption MakeSafe(this ThreadOption option)
    {
        if (option == ThreadOption.UIThread && _uiOverride != null)
            return (ThreadOption) _uiOverride;

        return option;
    }

}

ثم ، في جميع اشتراكات الحدث الخاصة بي ، أستخدم ما يلي:

EventAggregator.GetEvent<MyEvent>().Subscribe
(
    x => // do stuff, 
    ThreadOption.UiThread.MakeSafe()
);

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

[TestFixture]
public class ExampleTest
{
    [SetUp]
    public void SetUp()
    {
        ThreadingExtensions.UiOverride = ThreadOption.Background;
    }

    [Test]
    public void EventTest()
    {
        // This doesn't actually test anything useful.  For a real test
        // use something like a view model which subscribes to the event
        // and perform your assertion on it after the event is published.
        string result = null;
        object locker = new object();
        EventAggregator aggregator = new EventAggregator();

        // For this example, MyEvent inherits from CompositePresentationEvent<string>
        MyEvent myEvent = aggregator.GetEvent<MyEvent>();

        // Subscribe to the event in the test to cause the monitor to pulse,
        // releasing the wait when the event actually is raised in the background
        // thread.
        aggregator.Subscribe
        (
            x => 
            {
                result = x;
                lock(locker) { Monitor.Pulse(locker); }
            },
            ThreadOption.UIThread.MakeSafe()
        );

        // Publish the event for testing
        myEvent.Publish("Testing");

        // Cause the monitor to wait for a pulse, but time-out after
        // 1000 millisconds.
        lock(locker) { Monitor.Wait(locker, 1000); }

        // Once pulsed (or timed-out) perform your assertions in the real world
        // your assertions would be against the object your are testing is
        // subscribed.
        Assert.That(result, Is.EqualTo("Testing"));
    }
}

لجعل الانتظار والنبض أكثر إيجازًا ، أضفت أيضًا طرق التمديد التالية إلى Threadingextensions:

    public static void Wait(this object locker, int millisecondTimeout)
    {
        lock (locker)
        {
            Monitor.Wait(locker);
        }
    }

    public static void Pulse(this object locker)
    {
        lock (locker)
        {
            Monitor.Pulse(locker);
        }
    }

ثم يمكنني أن أفعل:

// <snip>
aggregator.Subscribe(x => locker.Pulse(), ThreadOption.UIThread.MakeSafe());

myEvent.Publish("Testing");

locker.Wait(1000);
// </snip>

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

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