如何为在循环内调用的此方法编写单元测试?
How do I write a unit test for this method that is called inside a loop?
我正在尝试使用 xUnit 和 Moq 测试一种方法。
public void DoSomeWork_Verify_GetJsonDataString_is_called()
测试失败。请有人告诉我我在 test/mocking 中遗漏了什么。 GetJsonDataString
是一个 API 调用 returns 一个 JSON 字符串。
public class A
{
private readonly IData _data;
private readonly IApiCall _apiCall;
public A (IData data, IApiCall apiCall)
{
_data = data;
_apiCall = apiCall;
}
public void DoSomeWork
{
var apps = _data.GetListOfApps(); // test on this method call is passing
foreach(var app in apps)
{
if(string.IsNullOrEmpty(app.AppId)) continue;
string jsonString = _apiCall.GetJsonDataString("ApiEndpoint" + app.AppId);
// how do I test this method call? Note: I'm not using async, this is just a console app.
}
}
}
//MyTest
public class TestA
{
private readonly Mock<IData> _data;
private readonly Mock<IApiCall> _apicall;
public TestA()
{
_data = new Mock<IData>;
_apiCall = new Mock<IApiCall>;
}
public void DoSomeWork_Verify_GetListOfApps_is_called()
{
_data.Setup(x => x.GetListOfApps());
var sut = new A(_data.Object, _apiCall.Object);
sut.DoSomeWork();
_data.Verify(x => x.GetListOfApps(), Times.AtLeastOnce); // this passes
}
public void DoSomeWork_Verify_GetJsonDataString_is_called()
{
_apicall.Setup(x => x.GetJsonDataString(It.IsAny<string>()));
var sut = new A(_data.Object, _apiCall.Object);
sut.DoSomeWork();
_apicall.Verify(x => x.GetJsonDataString(It.IsAny<string>()), Times.AtLeastOnce); // Failing
}
}
你需要模拟 GetListOfApps
以便它实际上 return 是你可以循环的东西。为此,您需要使用 Returns
方法。
假设方法 return 是 List<App>
您的设置可能如下所示:
_data.Setup(x => x.GetListOfApps())
.Returns(new List<App> { new App { AppId = "X" } });
现在您的方法将接收要循环的数据,并且将调用您尝试验证的方法。
请注意,在设置 GetJsonDataString
时,您并未为其指定 return 值。默认情况下,最小起订量将 return null
但您可能也应该使用 return 值来模拟它。
_data.Setup(x => x.GetJsonDataString(It.IsAny<string>()))
.Returns("some string");
还有 Returns
的重载接受工厂函数并将传递给模拟函数的参数作为输入。如果 want/need return 结果根据输入参数而变化,则可以使用它。
_data.Setup(x => x.GetJsonDataString(It.IsAny<string>()))
.Returns(strParam => "some string" + strParam);
现在,当您进行验证时,我建议您尽量不要使用 It.IsAny
。否则你就是在说你不关心如何它被调用。假设我们想验证它实际上是用上面的模拟值 (AppId="X"
) 调用的,你会这样做:
_apicall.Verify(x => x.GetJsonDataString("ApiEndPointX"), Times.Once);
现在您得到了一个测试,它不仅检查该方法是否被调用,而且实际验证它是否使用 预期 值调用。
旁注:您的测试 class 没有理由在其构造函数中接受参数,尤其是 Mock<>
类型的参数。你甚至没有使用它们!从技术上讲,这甚至应该无法执行,因为您没有设置 IClassFixture
。您可以完全摆脱构造函数(并直接分配字段)或只删除参数:
public TestA()
{
_data = new Mock<IData>();
_apiCall = new Mock<IApiCall>();
}
我正在尝试使用 xUnit 和 Moq 测试一种方法。
public void DoSomeWork_Verify_GetJsonDataString_is_called()
测试失败。请有人告诉我我在 test/mocking 中遗漏了什么。 GetJsonDataString
是一个 API 调用 returns 一个 JSON 字符串。
public class A
{
private readonly IData _data;
private readonly IApiCall _apiCall;
public A (IData data, IApiCall apiCall)
{
_data = data;
_apiCall = apiCall;
}
public void DoSomeWork
{
var apps = _data.GetListOfApps(); // test on this method call is passing
foreach(var app in apps)
{
if(string.IsNullOrEmpty(app.AppId)) continue;
string jsonString = _apiCall.GetJsonDataString("ApiEndpoint" + app.AppId);
// how do I test this method call? Note: I'm not using async, this is just a console app.
}
}
}
//MyTest
public class TestA
{
private readonly Mock<IData> _data;
private readonly Mock<IApiCall> _apicall;
public TestA()
{
_data = new Mock<IData>;
_apiCall = new Mock<IApiCall>;
}
public void DoSomeWork_Verify_GetListOfApps_is_called()
{
_data.Setup(x => x.GetListOfApps());
var sut = new A(_data.Object, _apiCall.Object);
sut.DoSomeWork();
_data.Verify(x => x.GetListOfApps(), Times.AtLeastOnce); // this passes
}
public void DoSomeWork_Verify_GetJsonDataString_is_called()
{
_apicall.Setup(x => x.GetJsonDataString(It.IsAny<string>()));
var sut = new A(_data.Object, _apiCall.Object);
sut.DoSomeWork();
_apicall.Verify(x => x.GetJsonDataString(It.IsAny<string>()), Times.AtLeastOnce); // Failing
}
}
你需要模拟 GetListOfApps
以便它实际上 return 是你可以循环的东西。为此,您需要使用 Returns
方法。
假设方法 return 是 List<App>
您的设置可能如下所示:
_data.Setup(x => x.GetListOfApps())
.Returns(new List<App> { new App { AppId = "X" } });
现在您的方法将接收要循环的数据,并且将调用您尝试验证的方法。
请注意,在设置 GetJsonDataString
时,您并未为其指定 return 值。默认情况下,最小起订量将 return null
但您可能也应该使用 return 值来模拟它。
_data.Setup(x => x.GetJsonDataString(It.IsAny<string>()))
.Returns("some string");
还有 Returns
的重载接受工厂函数并将传递给模拟函数的参数作为输入。如果 want/need return 结果根据输入参数而变化,则可以使用它。
_data.Setup(x => x.GetJsonDataString(It.IsAny<string>()))
.Returns(strParam => "some string" + strParam);
现在,当您进行验证时,我建议您尽量不要使用 It.IsAny
。否则你就是在说你不关心如何它被调用。假设我们想验证它实际上是用上面的模拟值 (AppId="X"
) 调用的,你会这样做:
_apicall.Verify(x => x.GetJsonDataString("ApiEndPointX"), Times.Once);
现在您得到了一个测试,它不仅检查该方法是否被调用,而且实际验证它是否使用 预期 值调用。
旁注:您的测试 class 没有理由在其构造函数中接受参数,尤其是 Mock<>
类型的参数。你甚至没有使用它们!从技术上讲,这甚至应该无法执行,因为您没有设置 IClassFixture
。您可以完全摆脱构造函数(并直接分配字段)或只删除参数:
public TestA()
{
_data = new Mock<IData>();
_apiCall = new Mock<IApiCall>();
}