SwimmingG
SwimmingG

Reputation: 664

Mocking ApiController

I'm trying to test a WebApi Controller that I have made. I have tried to use dependency injection to make the testing easier. Though its actually having the opposite effect.

I currently have a controller that takes a repo interface in its constructor. The repo interface takes a DbContext interface in its constructor too. Am i right in thinking that I need to mock the DbContext, and passed that mocked context as an argument when mocking the repo and then pass that mocked repo into the actual implementation of the controller that I am testing?

I am using Moq and NUnit.

Thanks

Upvotes: 1

Views: 1768

Answers (2)

Kritner
Kritner

Reputation: 13765

I'm assuming you're talking about unit testing since you're making use of mocks.

You do not need to mock deeper than the first level interfaces the class you're unit testing depends on. In your example, your controller depends on an interface let's call IRepository. Your implementation of IRepository in turn takes in a IDbContext. Note the bold/italics in the previous sentence, as long as you're mocking the interface that is IRepository, then IDbContext is not related in any way - IDbContext is a dependency of your concrete repository, but not your IRepository.

Your IRepository should have everything your controller needs for mocking data/behavior relevant to unit testing your controller.

Example:

public class MyController : MyController
{
    private readonly IRepository _repo;

    public MyController(IRepository repo)
    {
        _repo = repo;
    }

    public IActionResult GetData(string userId)
    {
        var data = _repo.GetUserInformation(userId);
        var someTransformation = null; // transform data
        return View(someTransformation);
    }
}

public interface IRepository
{
    MyObject GetData(string userId);
}

public class Repository : IRepository
{
    private readonly IDbContext _iDbContext;

    public Repository(IDbContext iDbContext)
    {
        _iDbContext = iDbContext;
    }

    public MyObject GetUserInformation(string userId)
    {
        return _iDbContext.MyObjects.Where(w => w.UserId == userId);
    }
}

public interface IDbContext
{
    // impl
}

public class DbContext : IDbContext
{
    // impl
}

For the purposes of testing MyController, you rely on interface IRepository. It does not matter in turn what your implementation of IRepository (Repository) relies on, as that is not relevant to the scope of testing MyController.

In your test:

public class MyControllerTests
{
    public void MyTest()
    {
        Mock<IRepository> mockRepo = new Mock<IRepository>();
        mockRepo
            .Setup(s => s.GetUserInformation(It.IsAny<string>())
            .Returns(new MyObject()
            {
                UserId = "whatever", // or whatever the mocked data needs to be
                DateCreated = DateTime.MinValue
            });
    }
}

In the above test, we are providing sample data the IRepository should return when GetUserInformation is invoked, we are not dependent on an actual DbContext, or IDbContext even, as the IRepository simply defines a contract of "when GetUserInformation is called with a string, it should return a MyObject". How it goes about doing that doesn't matter.

Upvotes: 4

Rob Prouse
Rob Prouse

Reputation: 22647

Trust your intuition. I assume that your DbContext implements an interface that gives you access to everything you need and that interface is what you pass into the constructor of your controller via DI.

In your test assembly, create a MockDbContext that implements the interface and returns known mock data. Then create an instance of your controller using your MockDbContext and run your tests against the controller.

If you have any problems, comment and I will try to help by updating the answer.

Upvotes: 1

Related Questions