模拟(MOQ)传递参数方法(WebAPI MVC控制器)

时间:2015-07-27 17:40:05

标签: c# asp.net-web-api moq

由于不了解此方案的技术名称,我深表歉意。我在嘲笑单元测试,这一切都很好。但是在这部分代码中,我遇到了一个超出我嘲弄知识的场景。基本上我有MethodA,它有3个参数。其中一个参数作为另一个方法的输出传递。

当我逐步执行作为参数传递的方法时

我的困难在于,在我的模拟对象之前正在执行传递的方法。现在它似乎是一个简单的解决方案......嘲笑第二种方法......这就是我的知识落空的地方。我不知道如何将“第二”方法模拟到测试环境中。

我的控制器正在测试(当然简化):

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...}
    }

}

我的测试:

[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.

}

所以我真的不想潜入MethodB,但无论如何它似乎都在做。我做错了什么?

TIA

感谢您的回复。我完全理解你在说什么。我正在尝试在重构之前获得一些测试覆盖率。那么有没有办法让methodB不执行,只是让我的repositoryK mock返回我在设置中指定的内容。

1 个答案:

答案 0 :(得分:2)

您的代码不容易测试,因为它对HttpClient有很强的依赖性。您已经很好地分离了存储库实现,但如果您想轻松测试代码,您还应该将调用Google API的代码分开。我的想法是这样的:

// 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);
    }
}

如果您有此类设置,则可以轻松模拟IRepositoryKIGoogleClient

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

但是,如果您希望保持代码紧密耦合,则可以通过较小的更改来模拟对MethodB的调用。

首先,您需要将方法MethodB设为虚拟,因此可以在模拟中重写:

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

然后在您的测试中,而不是实例化控制器,实例化并使用控制器的模拟:

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();