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

本文关键字:MVC WebAPI 控制器 方法 MOQ mock 参数 | 更新日期: 2023-09-27 18:04:51

我事先为不知道这个场景的技术名称而道歉。我正在嘲笑单元测试,这一切都很好。然而,在这段代码中,我遇到了一个超出我的模拟知识的场景。基本上我有一个接受3个参数的MethodA。其中一个参数作为另一个方法的输出传递。

当我遍历作为参数传递的方法时执行

我的困难是,传递的方法正在执行之前我的模拟对象。现在看来这是一个简单的解决方案……也模仿第二种方法……这就是我的知识不足之处。我不知道如何将"第二个"方法模拟到测试上下文中。

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

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返回我在设置中指定的内容呢?

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

您的代码不容易测试,因为它对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();