Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
603 views
in Technique[技术] by (71.8m points)

c# - MVVM light Dispatcher unit tests

I have the following function

 public void Reset()
 {
     DisableModule();
     DispatcherHelper.UIDispatcher.Invoke(() =>
     {
           PixelPointInfoCollection.Clear();
           PixelPointInfoCollection.Add(new PointViewModel());
     });
     _cachedPoints.Clear();
 }

The following code gets stuck in the Invoke() method, when running a unit test.

I saw some articles about creating a custom interface on dispatcher and mocking the dispatcher in unit tests. for example http://blog.zuehlke.com/en/mvvm-and-unit-testing/

Is there no other way? I have a huge code base.. do I really need to change everything now?

Update 18.8.2016 For now here is what I did and it is working

public static class AppServices
{

    public static IDispatcher Dispatcher { get; set; } 
    public static void Init(IDispatcher dispatcher)
    {
        Dispatcher = dispatcher;
    }
}

//this inteface is in order to overrcome MVVM light Dispatcher so we can mock it for unit tests
public interface IDispatcher
{
    void Invoke(Action action);
    void Invoke(Action action, DispatcherPriority priority);
    DispatcherOperation BeginInvoke(Action action);
}

public class DispatcherWrapper :IDispatcher
{
    public DispatcherWrapper()
    {
        DispatcherHelper.Initialize();
    }
    public void Invoke(Action action)
    {
        DispatcherHelper.UIDispatcher.Invoke(action);
    }

    public void Invoke(Action action, DispatcherPriority priority)
    {
        DispatcherHelper.UIDispatcher.Invoke(action, priority);
    }

    public DispatcherOperation BeginInvoke(Action action)
    {
       return DispatcherHelper.UIDispatcher.BeginInvoke(action);
    }
}

so inside the app.xaml.cs I call AppServices.Init(new DispatcherWrapper());

and in the unit tests I call AppServices.Init(Substitute.For());

using NSubstitute

Please comment if you thing I'm missing something, I'm worried about how do I make the mocking framework to run the actions I used to do inside the

DispatcherHelper.UIDispatcher.Invoke
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Unfortunately situation is as a result of an initial design issue which made the code base difficult to unit test. The difficulty in creating unit tests for code is directly related to how well the code is designed. The article you mentioned in your post is what you would need to do to make accessing the dispatcher mock-able as it (the dispatcher) is an implementation concern associated with the UI thread which will not be available during your unit tests. Hence the lock on Invoke

To quote the article you mentioned:

We are unable to test the code that uses App.Current.Dispatcher (since App.Current will be null during unit tests execution).

A possible solution would be to create an interface IDispatcher and a wrapper around App.Current.Dispatcher that implements that interface.

public interface IDispatcher {
    void Invoke(Action action);
    void BeginInvoke(Action action);
}

public class DispatcherWrapper : IDispatcher {
    Dispatcher dispatcher;

    public DispatcherWrapper() {    
        dispatcher = Application.Current.Dispatcher;        
    }
    public void Invoke(Action action) {
        dispatcher.Invoke(action);
    }

    public void BeginInvoke(Action action) {
        dispatcher.BeginInvoke(action);
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...