Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test using a ViewModelLocator

I've created a custom view model locator using Autofac and set it up normally through the App.xaml like most of them are usually used. My problem is how do I unit test now? I'm getting an error every time I try to test a method that initializes a view

In my app.xaml:

<desktop:ViewModelLocator xmlns:local="clr-namespace:MyProject.Desktop" x:Key="ViewModelLocator" />

In each view:

DataContext="{Binding MyFirstViewModel, Source={StaticResource ViewModelLocator}}"

The Unit Test Error:

{"Cannot find resource named 'ViewModelLocator'. Resource names are case sensitive."}

I understand why cause when you unit test, there really isn't an instance of the actual App so what is a good way around this problem?

ViewModelLocator Code:

/// <summary>
/// Autofac object container
/// </summary>
private readonly IContainer objectContainer;

#region Constructor

/// <summary>
/// Constructor for view model locator
/// </summary>
public ViewModelLocator()
{
    objectContainer = App.ObjectContainer;
    //objectContainer.BeginLifetimeScope();
}

#endregion

#region Properties

/// <summary>
/// Gets the resolved instance of a main window view model
/// </summary>
public MainWindowViewModel MainWindowViewModel
{
    get
    {
        return objectContainer.Resolve<MainWindowViewModel>();
    }
}

public FirstViewModel MyFirstViewModel 
{
    get
    {
        return objectContainer.Resolve<FirstViewModel>();
    }
}

public SecondViewModel MySecondViewModel 
{
    get
    {
        return objectContainer.Resolve<SecondViewModel>();
    }
}
like image 542
TMan Avatar asked Nov 11 '22 15:11

TMan


1 Answers

This is a bit late, but maybe useful. Instead of resolving objectContainer in the constructor, do it through the property:

//note this is a lazy getter, i.e. will be resolved when needed on the first call
private IContainer ObjectContainer
{
   get
   {
       if(objectContainer == null)
           objectContainer = App.ObjectContainer;
       return objectContainer:
   }
}

Then use the property through your code, not the field. Also when I am concerned about someone else using the field that I want to enforce through the property usage, I would rename it to something that would not easily be recognizable in the IntelliSence (zREgdnlksfObjectContainer for example:) ) Note the property is private, so nothing really changes. You can make the property internal and mark your lib to be visible to your unit test, so that in the unit test you can Mock it to WhenCalled() return/resolve IContainer.

like image 75
denis morozov Avatar answered Nov 15 '22 10:11

denis morozov