0

I always try to avoid the use of Service Locator, but in this case I have models that inherit from Base and raises events (e.g. INotifyPropertyChanged) which I want to always dispatch on the UI Thread. I can't use a DI container to keep the model constructor empty.

Below my code.

    public abstract class Base
    {
        private static IMainThreadDispatcherService _dispatcherService;

    /// <summary>
    /// The main thread dispatcher.
    /// </summary>
    protected static IMainThreadDispatcherService DispatcherService
    {
        get
        {
            return _dispatcherService ??
                   (_dispatcherService = DependencyLocatorService.Resolve<IMainThreadDispatcherService>());
        }
    }

    /// <summary>
    /// Helper method to raise PropertyChanged events.
    /// </summary>
    /// <typeparam name="T">Type of the property.</typeparam>
    /// <param name="selectorExpression">The expression to pass the property name.</param>
    public virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
    {
        if (selectorExpression == null)
        {
            throw new ArgumentNullException("selectorExpression");
        }

        MemberExpression body = selectorExpression.Body as MemberExpression;
        if (body == null)
        {
            throw new ArgumentException("The body must be a member expression");
        }

        DispatcherService.RequestMainThreadAction(() =>
        {
            NotifyPropertyChanged(selectorExpression);
        });
    }
}

/// <summary>
/// A service that holds on the UI thread dispatcher.
/// </summary>
public interface IMainThreadDispatcherService
{
    /// <summary>
    /// Invoke an action on main thread.
    /// </summary>
    /// <param name="action">The Action to invoke.</param>
    /// <returns>True if successfully invoked.</returns>
    bool RequestMainThreadAction(Action action);
}

/// <summary>
/// This class is an abstraction of the service locator.
/// </summary>
public class DependencyLocatorService : IDependencyLocatorService
{
    /// <summary>
    /// Constructs a new object instance injecting all required dependencies.
    /// </summary>
    /// <typeparam name="T">Type of.</typeparam>
    /// <returns>The object instance constructed.</returns>
    public T IocConstruct<T>()
    {
        // A resolver
    }

    /// <summary>
    /// Resolves an instance of the specified type.
    /// </summary>
    /// <typeparam name="T">Type of.</typeparam>
    /// <returns>The object instance.</returns>
    public static T Resolve<T>() where T : class
    {
        try
        {
            // A resolver
        }
        catch(Exception)
        {}
        return null;
    }

    /// <summary>
    /// Registers a singleton instance of type T.
    /// </summary>
    /// <typeparam name="T">The type to register.</typeparam>
    /// <param name="instance">The instance.</param>
    public static void RegisterSingleton<T>(T instance) where T : class
    {
        try
        {
            // A resolver
        }
        catch (Exception)
        {
        }
    }
}

The problem is when I want to unit test the models that inherit from Base things start to get difficult.

I'm seeking to change the architecture to enable proper unit testing and mock the method DispatcherService.RequestMainThreadAction, but still raise the event. I'm using the Moq framework and not sure if I can setup somehow this kind of mocking since I want the original Action to be invoked.

George Taskos
  • 8,324
  • 18
  • 82
  • 147

2 Answers2

1

Unless you actually need a Mock, why don't you just use a simple stub?

public class DispatcherServiceStub : IMainThreadDispatcherService 
{
  public bool RequestMainThreadAction(Action action)
  {
    action();
  }
}
Ethan Cabiac
  • 4,943
  • 20
  • 36
  • Yeah, if I will compromise and make the DispatcherService property public and set it to the stub in the unit test setup, I'm away of the machine now, will test tomorrow. I don't see another way with keeping the property readonly and protected. – George Taskos Nov 09 '15 at 22:53
  • @GeorgeTaskos you don't need to make the property public since you are resolving the value via the `DependencyLocatorService`. – Ethan Cabiac Nov 10 '15 at 13:23
1

You can try to use the Callback method in your Moq setup and invoke the action in it?

Carlos Torrecillas
  • 4,965
  • 7
  • 38
  • 69
  • Please, if your schedule allows it, an example of your thought would be appreciated to make me understand it. – George Taskos Nov 09 '15 at 22:56
  • I'm on my mobile right now but you can check this page out: https://github.com/Moq/moq4/wiki/Quickstart or this one too http://stackoverflow.com/questions/2494930/settings-variable-values-in-a-moq-callback-call . Basically when you set up your method to be mocked you do .Callback((Action a) => a()); which means that the action you pass in gets run. Does it make sense? – Carlos Torrecillas Nov 09 '15 at 23:02
  • Yeah, it make sense but I might don't undestand, because this is not what I need. What I want is to mock the dependency, IMainThreadDispatcherService property but still in the SUT, which is any Base derive class to still run the original Action, NotifyPropertyChanged(selectorExpression). – George Taskos Nov 10 '15 at 00:28
  • If I understand the problem correctly you still want to run the NotifyPropertyChanged that is passed to the DispatcherService being that a mock. If that's the case when you do your set up of the method of IMainThreadDispatcherService you can set up the Callback for that method and tell the mock to run the action you pass it. In this case you would run the NotifyPropertyChanged because you configure your mock to have some behavior. – Carlos Torrecillas Nov 10 '15 at 09:00
  • Both solutions work fine, I selected this one because I can use Mock. My original problem was the MvvmCross setup for testing (I don't like it, but you live with legacy code you inherit). Then everything worked perfect. – George Taskos Nov 10 '15 at 14:46