0
votes

My apologies in advanced for not knowing the technical name of this scenario. I am mocking for unit test and that is all fine. However on this section of code I have run into a scenario that exceeds my mocking knowledge. Basically I have MethodA that takes 3 parameters. One of the parameters is passed as another method's output.

When I step through the method passed as a parameter is executed

My difficulty is that the passed method is being executed BEFORE my mocked object. Now it seems like a simple solution...mock the second method as well...that is where my knowledge falls down. I don't know how to get the "second" method mock into the testing context.

My controller being tested (simplified of course):

public class OrderController : ApiController
{

    public OrderController(IRepositoryK repositoryk)
    {}

    public HttpResponseMessage NewOrder()
    {
       ...snip....
       string x = repositoryk.MethodA("stuff", "moreStuff", MethodB("junk"));

    }

    public string MethodB(string data)
    {
       using (var client = new HttpClient())
       {...make call to Google API...}
    }

}

My test:

[TestMethod]
public void AddOrder_CorrectResponse()
{

   private Mock<IRepositoryK> _repK = new Mock<IRepositoryK>();
   _repK.Setup(x => x.MethodA(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
   .Returns("Yippe");

  //of course I've left out all the controller buildup and execution stuff.

}

So I really have no desire to dive into MethodB but it seems to be doing it anyway. What am I doing wrong?

TIA


Thank you for your responses. I understand completely what you are saying. I'm trying to get some testing coverage in place before refactoring. So is there no way of keeping methodB from executing and just let my repositoryK mock just return what I've specified in the setup.

1
you won't be able to easily mock MethodB as it's in the same class rather than in a mockable dependency - Matthew Mcveigh
Your method B uses an Httpclient. You should pull this into it's own repo (google api) and pass that in the constructor via DI as well. In order to test the functionality of a class, all references to external resources (databases, queues, web services) should be mocked out and passed in via DI. Your MethodB should use this google api repo and it shouldn't care if it's the real google api repo, or a mocked out version - Steve's a D
@GPGVM Yes, there is a way to keep MethodB from executing - check out the second part of my answer for a way how to do it - dotnetom

1 Answers

2
votes

Your code is not easy to test, because it has hard dependency on HttpClient. You have nicely separated repository implementation, but if you want to easily test the code you should also separate code which calls Google API. The idea is to have something like this:

// Add interfece for accessing Google API
public interface IGoogleClient
{
    string GetData(string data);
}

// Then implementation is identical to MethodB implementation:
public class GoogleClient : IGoogleClient
{
    public string GetData(string data)
    {
        using (var client = new HttpClient())
        {
        //...make call to Google API...
        }
    }
}

// Your controller should look like this:
public class OrderController : ApiController
{
    private readonly IRepositoryK repositoryk;
    private readonly IGoogleClient googleClient;

    public OrderController(IRepositoryK repositoryk, IGoogleClient googleClient)
    {
        this.googleClient = googleClient;
        this.repositoryk = repositoryk;
    }

    public HttpResponseMessage NewOrder()
    {
       //...snip....
       string x = repositoryk.MethodA("stuff", "moreStuff", MethodB("junk"));
    }

    public string MethodB(string data)
    {
        return googleClient.GetData(data);
    }
}

If you have such setup you can easily mock both IRepositoryK and IGoogleClient:

Mock<IRepositoryK> repK = new Mock<IRepositoryK>();
Mock<IGoogleClient> googleClient = new Mock<IGoogleClient>();
repK.Setup(x => x.MethodA(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Returns("Yippe");
googleClient.Setup(It.IsAny<string>()).Returns("something");
var controller = new OrderController(repK.Object, googleClient.Object);
// Test what you want on controller object

However, if you want to keep your code tightly coupled you can mock the call to MethodB with small changes.

First, you need to make method MethodB virtual, so it could be overridden in mock:

public virtual string MethodB(string data)
{
    // your code
}

Then in your test instead of instantiating controller, instantiate and use mock of your controller:

var repK = new Mock<IRepositoryK>();
// create mock and pass the same constructor parameters as actual object
var controllerMock = new Mock<OrderController>(repK.Object);
controllerMock.CallBase = true;
// mock MethodB method:
controllerMock.Setup(x => x.MethodB(It.IsAny<string>())).Returns("data");
// call the method on mock object
// instead of calling MethodB you will get a mocked result
var result = controllerMock.Object.NewOrder();