Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock nested dependencies with MOQ

I assumed that MOQ would automatically create Mocks for any nested dependencies.

I am unit testing an ASP.Net MVC Controller:

public class TransactionController : Controller
{
    private readonly ITransactionService _transactionService;
    private readonly SearchPanelVmBuilder _searchPanelVmBuilder;
    private readonly TransactionVmsBuilder _transactionVmsBuilder;

    public TransactionController(TransactionVmsBuilder transactionVmsBuilder, ITransactionService transactionService, SearchPanelVmBuilder searchPanelVmBuilder)
    {
        _transactionVmsBuilder = transactionVmsBuilder;
        _transactionService = transactionService;
        _searchPanelVmBuilder = searchPanelVmBuilder;
    }

    // other methods omitted for brevity

    public PartialViewResult SearchPanel()
    {
        var vm = _searchPanelVmBuilder.BuildVm();

        return PartialView("_SearchPanel", vm);
    }
}

The unit test code:

[Fact]
public void SeachPanel_Calls_BuildSearchPanelVm()
{
    // Arrange
    var mockTransService = new Mock<ITransactionService>();
    var mockTransVmsBuilder = new Mock<TransactionVmsBuilder>();
    var mockSearchPanelVmBuilder = new Mock<SearchPanelVmBuilder>();
    var controller = new TransactionController(mockTransVmsBuilder.Object, mockTransService.Object, mockSearchPanelVmBuilder.Object);

    // Act
    controller.SearchPanel();

    // Assert
    mockSearchPanelVmBuilder.Verify(x => x.BuildVm());
}

MOQ complains:

Can not instantiate proxy of class: MCIP.Web.UI.ViewModelBuilders.Singular.SearchPanelVmBuilder. Could not find a parameterless constructor.

The class it can't instantiate a proxy for:

public class SearchPanelVmBuilder
{
    private readonly ITransactionTypeService _transactionTypeService;
    private readonly TransactionTypeVmBuilder _transactionTypeVmBuilder;
    private readonly UserProvider _userProvider;

    public SearchPanelVmBuilder(
        UserProvider userProvider,
        ITransactionTypeService transactionTypeService,
        TransactionTypeVmBuilder transactionTypeVmBuilder
        )
    {
        _userProvider = userProvider;
        _transactionTypeService = transactionTypeService;
        _transactionTypeVmBuilder = transactionTypeVmBuilder;
    }

    public virtual SearchPanelVm BuildVm()
    {
        return new SearchPanelVm
        {
            Userlist = _userProvider.GetOperators(),
            TransactionTypes =
                _transactionTypeService.GetAll().Select(x => _transactionTypeVmBuilder.BuildVmFromModel(x)).ToList()
        };
    }
}

Its corresponding dependencies:

public class UserProvider
{
    private static int retryCount;

    public virtual List<string> GetOperators()...

    public virtual List<string> GetGroupsForUser(WindowsIdentity identity)...
}

public interface ITransactionTypeService
{
    List<TransactionType> GetAll();
}

public class TransactionTypeVmBuilder
{
    public virtual TransactionTypeVm BuildVmFromModel(TransactionType transactionType)...
}

Am I doing something wrong?

Do I have to explicitly tell MOQ to have a go at auto-mocking nested dependencies?

Or do i have to explicitly set up the nested Mocks - like this:

var mockUserProvider = new Mock<UserProvider>();
var mockTransTypeService = new Mock<ITransactionTypeService>();
var mockTransactionTypeVmBuilder = new Mock<TransactionTypeVmBuilder>();
var mockSearchPanelVmBuilder = new Mock<SearchPanelVmBuilder>(mockUserProvider.Object, mockTransTypeService.Object, mockTransactionTypeVmBuilder.Object);
like image 811
JTech Avatar asked Jan 28 '16 02:01

JTech


2 Answers

Yes your assumption is correct. Because the class SearchPanelVmBuilder haven't provided parameterless constructor Mock for this class can't be created like this:

var mockSearchPanelVmBuilder = new Mock<SearchPanelVmBuilder>()

Causes exception: Could not find a parameterless constructor.


Instead create the Mock by providing all the parameters, something like this:

UserProvider userProvider = new UserProvider();
Mock<ITransactionTypeService> transactionTypeService = new Mock<ITransactionTypeService>();
TransactionTypeVmBuilder transactionTypeVmBuilder = new TransactionTypeVmBuilder();

// Use constructor with parameters here because SearchPanelVmBuilder 
// doesn't have parameterless constructor
var mockSearchPanelVmBuilder = new Mock<SearchPanelVmBuilder>(
    userProvider, transactionTypeService.Object, transactionTypeVmBuilder);

var mockTransService = new Mock<ITransactionService>();
var mockTransVmsBuilder = new Mock<TransactionVmsBuilder>();

var controller = new TransactionController(
    mockTransVmsBuilder.Object, 
    mockTransService.Object, 
    mockSearchPanelVmBuilder.Object);

Then the instance of the controller TransactionController can be created and the method SearchPanel can be called on it.

like image 176
Daniel Dušek Avatar answered Nov 13 '22 15:11

Daniel Dušek


In this case, all you want to test is that BuildVM is called and the result is returned so you don't need to mock the inner dependencies. You do need to setup a return value for the BuildVM method in the Arrange section before you call the SearchPanel method.

mockSearchPanelVmBuilder.Setup(x => x.BuildVM()).Returns(mockSearchPanelVM);

Because your mocking a class and virtual method if you don't setup a return value the actual implementation will be run. With an interface an error will be thrown that indicated you should setup a return value.

like image 1
chief7 Avatar answered Nov 13 '22 13:11

chief7