问题描述
const char*
测试这个控制器的测试用例如下:
public class ValuesController : Controller
{
private readonly IService fService;
public ValuesController( IService fService)
{
this.fService = fService;
}
public IActionResult InsertDetails( [FromForm]Details details,IFormFile file)
{
bool result = fService.SaveDetails(details,file);
return OK();
}
}
即使我模拟 public class ValuesControllerTests
{
readonly Mock<IService> mockService = new Mock<IService>();
[TestMethod]
public void SaveDetails_Test_Success()
{
var details = GetMockDetails(); // gets the sample DTO values
mockService.Setup(x => x.SaveDetails(details,GetFile())).Returns(true); ---- this is returning false even after mocking the value
var controller = new ValuesController(mockService.Object);
controller.ControllerContext.HttpContext = new DefaultHttpContext();
var res = controller.InsertDetails(details,GetFile()) as OkObjectResult;
Assert.AreEqual("File Details Saved Successfully",res.Value.ToString());
Assert.AreEqual(200,res.StatusCode);
}
IFormFile GetFile()
{
return new FormFile(new MemoryStream(Encoding.UTF8.GetBytes("This is a dummy file")),"Data","dummy.txt");
}
}
方法返回 SaveDetails
,它也总是返回 true
。
谁能告诉我我在这里做错了什么?非常感谢。
解决方法
期望是使用与调用被测成员时使用的不同的 IFormFile
(引用/实例)设置的。这会导致模拟返回被调用成员的默认值,该值是 false
,因为它是一个布尔值。
当调用 GetFile()
时,它每次都返回一个新实例。
所以要么使用相同的实例,就像使用 details
public void SaveDetails_Test_Success() {
Details details = GetMockDetails();
IFormFile file = GetFile(); //single instance
mockService.Setup(x => x.SaveDetails(details,file)).Returns(true); //used here
ValuesController controller = new ValuesController(mockService.Object);
controller.ControllerContext.HttpContext = new DefaultHttpContext();
var res = controller.InsertDetails(details,file) as OkObjectResult; //and here
Assert.AreEqual("File Details Saved Successfully",res.Value.ToString());
Assert.AreEqual(200,res.StatusCode);
}
或者使用参数匹配器(如 It.IsAny<>()
public void SaveDetails_Test_Success() {
Details details = GetMockDetails();
mockService.Setup(x => x.SaveDetails(details,It.IsAny<IFormFile>())).Returns(true); //<-- NOTE
ValuesController controller = new ValuesController(mockService.Object);
controller.ControllerContext.HttpContext = new DefaultHttpContext();
var res = controller.InsertDetails(details,GetFile()) as OkObjectResult;
Assert.AreEqual("File Details Saved Successfully",res.StatusCode);
}
,
您的 Setup
的问题在于它过于具体。
请记住,参数匹配是基于类的引用检查。
mockService
.Setup(service => service.SaveDetails(It.IsAny<Details>(),It.IsAny<IFormFile>()))
.Returns(true);
如果您想验证通话,则需要
保存 GetFile
的结果,然后进行断言:
public void SaveDetails_Test_Success()
{
//Arrange
var details = GetMockDetails();
var file = GetFile(); //same instance is used everywhere
mockService
.Setup(x => x.SaveDetails(It.IsAny<Details>(),It.IsAny<IFormFile>()))
.Returns(true);
var controller = new ValuesController(mockService.Object);
controller.ControllerContext.HttpContext = new DefaultHttpContext();
//Act
var result = controller.InsertDetails(details,file);
//Assert
var res = Assert.IsAssignableForm<OkObjectResult>(result); //Here we test the assignability as well
Assert.AreEqual("File Details Saved Successfully",res.Value.ToString());
//Assert.AreEqual(200,res.StatusCode); //This assert is unnecessary
mockService
.Verify(x => x.SaveDetails(details,file),Times.Once);
}
我建议不要将控制器操作的响应转换为 OkObjectResult
,我建议也声明它。 (就像我在上面的测试用例中所做的一样)