[英]How to create a unit test for an IActionResult controller action which returns decimal?
我是 unittest 的新手,也很困惑我希望你能帮助我理解几件事,我想为以下场景编写一个 Xunit 测试:
我有以下控制器:
[ApiController]
[Route("api/[controller]")]
public class HomeController : ControllerBase
{
public readonly IDataService _dataService;
public HomeController(IDataService dataservice)
{
_dataService = dataservice;
}
[HttpGet("/value")]
public IActionResult GetTotal(string name, string year)
{
var totalAmount = _dataService.GetToTalValue(name, year);
return Ok(totalAmount.Result);
}
我有存储库类可以与数据库交谈,我将其命名为数据服务:
public interface IDataService
{
public Task<decimal> GetTotal(string name, string year)
}
实现很简单,我只是写下我返回的内容来制作长篇故事:
public Task<decimal> GetTotal(string name, string year){
///here i connect to db and get data and return to controller
return Task.FromResult(totalAmount);
}
最后,我按照一些在线教程创建了一个单元测试:
public class UnitTest1
{
[Fact]
public void Test1()
{
var sut = new Mock<IDataService>();
sut.Setup(s => s.GetTotal("ASK", "1235")).ReturnsAsync(1231);
HomeController mm = new HomeController(sut.Object);
var result = mm.GetTotal("ASK", "1235");
///asset gives error because cant convert iactionresult to decimal
}
}
}
首先我不明白为什么我们需要写:
sut.Setup(s => s.GetTotal("ASK", "1235")).ReturnsAsync(1231);
因为我们将再次使用控制器进行确切的操作:
HomeController mm = new HomeController(sut.Object);
var result = mm.GetTotal("ASK", "1235");
该设置意味着我将两个值传递给 GetTotal 并期望 1231 对吗?那么测试控制器的意义何在? 请帮助我理解它,我的第二个问题是我从 iactionresult 得到的结果是十进制但在断言中我得到错误,我无法将 iactionresult 转换为十进制
任何帮助将不胜感激
如果你模拟一个接口,你没有测试接口中方法的结果,你认为数据服务正在工作并强制结果。 这应该是
sut.Setup(s => s.GetToTalValue("ASK", "1235")).ReturnsAsync(10);
您必须围绕此接口测试其他逻辑。 例如,如果控制器正确传递参数并返回正确值
[Fact]
public void Test1()
{
decimal resultTotal = 10;
string name = "ASK";
string year = "1235";
var mockDataService = new Mock<IDataService>();
mockDataService.Setup(s => s.GetToTalValue(name, year)).ReturnsAsync(resultTotal);
HomeController sut = new HomeController(mockDataService.Object);
IActionResult result = sut.GetTotal(name, year);
Assert.True(result is OkObjectResult);
Assert.True((decimal)((result as OkObjectResult)?.Value) == resultTotal);
}
第一个问题很自以为是,这是一个非常简单的Action,我只是为了“测试”而编写测试和一些回归测试,如果添加更多逻辑,我肯定会为更改编写测试。
第二个问题, OK()
是OkObjectResult的便捷方法,因此您可以将其转换为:
var result = (OkObjectResult) mm.GetTotal("ASK", "1235");
Assert.True(result.Value == 1231);
此外,您正在嘲笑IDataService
并将其sut
, sut 将是控制器, SUT通常不会被嘲笑或存根,这又是自以为是... :-)
鉴于服务被定义为异步,该操作也应该是异步的
[HttpGet("/value")]
public async Task<IActionResult> GetTotal(string name, string year) {
decimal totalAmount = await _dataService.GetToTalValue(name, year);
return Ok(totalAmount);
}
否则保持一切同步
对于单元测试,您正在单独验证被测对象的预期行为。
同样,由于主题是异步的,那么测试也应该是异步的
public class UnitTest1 {
[Fact]
public async Task Test1() {
//Arrange
string name = "ASK";
string year = "1235";
decimal expected = 1231;
//setup expectations of the mocked dependency
var mock = new Mock<IDataService>();
mock.Setup(_ => _.GetTotalValue(name, year)).ReturnsAsync();
HomeController sut = new HomeController(mock.Object);
//Act
IActionResult result = await sut.GetTotal(name, year);
//Need to convert to actual action result Type
OkObjectResult okResult = result as OkObjectResult;
//Assert
okResult.Should().NotBeNull();
//need to extract the actual value within the result
decimal actual = (decimal)okResult.Value;
//verify expected behavior
actual.Should().Be(expected); //using FluentAssertions.
}
}
由于给出的示例的简单性质,这已经被简化,但这里的最终目标是通过给它已知值和断言预期结果来验证被测对象的预期行为。
由于在这种情况下被测对象是控制器,因此对于此隔离测试的目的,实际接口的实现无关紧要。 模拟所需的行为并将模拟的依赖项赋予控制器(主体)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.