简体   繁体   中英

How can mock dependencies to Web API controller unit test?

In my current MVC application, I have architected a series of command objects to handle business actions. These business actions would be wrapped around service endpoints. These endpoints would also be consumed by an MVC frond-end & a windows app. Every business action will call into a DAO action, which in turn, calls into the required data access repositories to successfully perform the business action. I have listed an example action below.

Busines Action

public class CreateProjectAction
{
    IInsertProjectDAOAction InsertProjectDAOAction { get; set; }

    public void Execute()
    {
        // Does some business validation & other logic before
        //  calling the DAO action
        InsertProjectDAOAction.Execute();
    }
}

DAO Action

public interface IInsertProjectDAOAction
{
    void Execute();
}

public class InsertProjectDAOAction
{
    IProjectRepository ProjectRepository { get; set; }

    public void Execute()
    {
        ProjectRepository.Insert();
    }
}

Project Repository

public interface IProjectRepository 
{
    void Insert(Project proj);

    // other db methods would be listed here
}

public class ProjectRepository
{
    public void Insert(Project proj)
    {
        // Insert into the data store
    }
}

Controller

[HttpPost]
public IHttpActionResult Create(NewProjectModel newProjectModel)
{
    var cmdArgs = Mapper.Map<CreateProjectCommand.CreateProjectCommandArgs>(newProjectModel);

    var action = new CreateProjectCommand(UserId, cmdArgs);
    action.Execute();

    if(action.IsSuccessful)
        return Ok(project)
    else
        return InternalServerError(action.Exception);
}

Unit Test

public void InsertWith_ExistingProjectName_Returns_ServerError()
{
    var arg = new CreateProjectCommandArgs(){ .... };
    var cmd = CreateProjectAction(args);
    action.Execute();

    Assert.That(action.IsSuccessful, Is.False);
    Assert.That(action.Exception, Is.TypeOf<UniqueNameExcepton>());
}

I am using Ninject to assist with the dependency injection between layers. I have a bunch of unit tests around the business 'CreateProjectAction' to test out expected behavior of that object. The business actions are wrapped around a series of Web API service endpoints. I would also like to write tests around my MVC controllers so that I can be sure they work as planned.

I like the architecure so far, but having trouble figuring out how to mock the DAO action properties in the business action when writing unit tests for the mvc controller. I'd love to hear suggestions, other viewpoints, etc ...

Your question is still a bit unclear. It seems likely for example that InsertProjectDAOAction implements the interface IInsertProjectDAOAction , even though your sample code doesn't indicate that it does. It's also unclear what CreateProjectCommand in your controller example is, since it isn't one of your example elements above it.

That said, one approach that you can take is to defer the creation of your commands out to a factory and inject the factory into your controller (through Ninject in your code and as a Mock in your unit tests). This allows you setup a mock chain. You mock the factory and have it return a mock of your action that you're interested in, which you can then setup to do whatever you want. At a very basic level, this might look like this:

public interface ICommandFactory {
    IInsertProjectDAOAction CreateInsertProjectAction(int userId);
}

public class CommandFactory : ICommandFactory{
    public IInsertProjectDAOAction CreateInsertProjectAction(int userId) {
        return new InsertProjectDAOAction(/* userId???? */);
    }
}

The controller would do something like this to use the factory:

public IHttpActionResult Create(/* ... */) {
    var action = _commandFactory.CreateInsertProjectAction(1234);
    action.Execute();
    // ...
}

With a test looking something like:

[Test]
public void MyTest() {
    var factoryMock = new Mock<ICommandFactory>();
    var commandMock = new Mock<IInsertProjectDAOAction>();

    factoryMock.Setup(x => x.CreateInsertProjectAction(It.IsAny<int>())).Returns(commandMock.Object);
    commandMock.Setup(x => x.Execute()).Throws(new InvalidOperationException("Random failure"));

    var controller = new MyController(factoryMock.Object);

    try {
        controller.Create(/* ... */);
        Assert.Fail();
    }
    catch (InvalidOperationException ex) {
        Assert.AreEqual("Random failure", ex.Message);
    }
}

This is a general approach that you could take. However, as I've said, that might not be right for your situation, because your question is unclear. I've also ignored other issues about how you create / test your controller in general since that doesn't seem to be what your question is about...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM