9

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);
JTech
  • 3,420
  • 7
  • 44
  • 51
  • Personally, I try to have my classes depend on interfaces rather than concrete classes, so I can mock my heart out during unit testing and really limit the scope of each test. – ESG Jan 28 '16 at 02:55
  • In short, having interfaces won't solve the problem. The classes that don't have interfaces have virtual methods - they can still be mocked out just the same as an interface is. Since there will never be an alternative implementation for them, interfaces purely to allow mocking, are unneccessary. – JTech Jan 28 '16 at 03:12

2 Answers2

3

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.

Daniel Dušek
  • 13,683
  • 5
  • 36
  • 51
  • Thanks for the answer! So there is really no way for Moq to automatically resolve nested dependencies, like StructureMap would? – JTech Jan 29 '16 at 02:58
  • You are welcome! As far as I understand how constructors work in c#, no there isn't. But check e.g. [this answer](http://stackoverflow.com/questions/5262881/mocking-objects-without-no-argument-constructor-in-c-sharp-net). Or [this](http://stackoverflow.com/questions/25649155/can-not-instantiate-proxy-could-not-find-a-parameterless-constructor). – Daniel Dušek Jan 29 '16 at 07:35
1

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.

chief7
  • 14,263
  • 14
  • 47
  • 80