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返回我在设置中指定的内容呢?
您的代码不容易测试,因为它对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);
}
}
如果您有这样的设置,您可以轻松地模拟IRepositoryK
和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
然而,如果你想保持你的代码紧密耦合,你可以模拟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();