简体   繁体   English

如何为返回十进制的 IActionResult 控制器操作创建单元测试?

[英]How to create a unit test for an IActionResult controller action which returns decimal?

Im new to unittest and also confused i hope you help me understand couple of things, I would like to write a Xunit test for the following scenario:我是 unittest 的新手,也很困惑我希望你能帮助我理解几件事,我想为以下场景编写一个 Xunit 测试:

I have a following controller:我有以下控制器:

    [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);
        }

i have repository class to talk to db i named it dataservice:我有存储库类可以与数据库交谈,我将其命名为数据服务:

 public  interface IDataService
{
  
    public Task<decimal> GetTotal(string name, string year)
}

the implemnation is simple i just write what im returning to make the long story shot:实现很简单,我只是写下我返回的内容来制作长篇故事:

        public Task<decimal> GetTotal(string name, string year){
    ///here i connect to db and get data and return to controller 

     return Task.FromResult(totalAmount);
    }

at the end,i have created a unit test by following some online tutorials:最后,我按照一些在线教程创建了一个单元测试:

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
    }
}

} }

first i of all i don't get why do we need to write :首先我不明白为什么我们需要写:

  sut.Setup(s => s.GetTotal("ASK", "1235")).ReturnsAsync(1231);

because we are going to the exact thing with controller again:因为我们将再次使用控制器进行确切的操作:

 HomeController mm = new HomeController(sut.Object);
 var result = mm.GetTotal("ASK", "1235");

that setup means im passing two values to the GetTotal and expect 1231 right?whats the point of testing the controller then?该设置意味着我将两个值传递给 GetTotal 并期望 1231 对吗?那么测试控制器的意义何在? Please help me understand it and my second question is the result i get from iactionresult is decimal but in assert i get error which i cant convert iactionresult to decimal请帮助我理解它,我的第二个问题是我从 iactionresult 得到的结果是十进制但在断言中我得到错误,我无法将 iactionresult 转换为十进制

any help will be highly appreciated任何帮助将不胜感激

if you mock an interface you haven't to test the results of methods in the interface, you think that dataservice is working and force the result.如果你模拟一个接口,你没有测试接口中方法的结果,你认为数据服务正在工作并强制结果。 this should be这应该是

sut.Setup(s => s.GetToTalValue("ASK", "1235")).ReturnsAsync(10);

you have to test other logic around this interface.您必须围绕此接口测试其他逻辑。 for example if the controller pass correctly the parameters and return the correct value例如,如果控制器正确传递参数并返回正确值

[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);
}

First question is very opinionated, and this is a very simple Action, I'd only write a test for "testing" sake and some regression testing, if more logic is added, I'd for sure write tests for the changes.第一个问题很自以为是,这是一个非常简单的Action,我只是为了“测试”而编写测试和一些回归测试,如果添加更多逻辑,我肯定会为更改编写测试。

Second question, OK() is a convenience method for OkObjectResult , so you can cast it as so:第二个问题, OK()OkObjectResult的便捷方法,因此您可以将其转换为:

var result = (OkObjectResult) mm.GetTotal("ASK", "1235");

Assert.True(result.Value == 1231);

Also, you are mocking IDataService and calling it sut , sut would be the controller, a SUT usually isn't mocked or stubbed, again this is opinionated... :-)此外,您正在嘲笑IDataService并将其sut , sut 将是控制器, SUT通常不会被嘲笑或存根,这又是自以为是... :-)

Given that the service is defined as async, that action should be async as well鉴于服务被定义为异步,该操作也应该是异步的

    [HttpGet("/value")]
    public async Task<IActionResult> GetTotal(string name, string year) {
        decimal totalAmount = await _dataService.GetToTalValue(name, year);
        return Ok(totalAmount);
    }

otherwise keep everything synchronous否则保持一切同步

For unit testing, you are verifying the expected behavior of the subject under test in isolation.对于单元测试,您正在单独验证被测对象的预期行为。

Again since the subject is async, then the test should be async as well同样,由于主题是异步的,那么测试也应该是异步的

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.
    }
}

This has been simplified because of the simple nature of the example given, but the end goal here is to verify the expected behavior of the subject under test by giving it known values and asserting expected results.由于给出的示例的简单性质,这​​已经被简化,但这里的最终目标是通过给它已知值和断言预期结果来验证被测对象的预期行为。

Since the subject under test is the controller in this case, then the implementation of the actual interface is of no concern for the purposes of this isolated test.由于在这种情况下被测对象是控制器,因此对于此隔离测试的目的,实际接口的实现无关紧要。 Mock the desired behavior and give that mocked dependency to the controller (the subject).模拟所需的行为并将模拟的依赖项赋予控制器(主体)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 单元测试返回 IActionResult 的控制器方法 - Unit testing controller methods which return IActionResult 如何单元测试一个返回视图的GlassController动作模型 - How to Unit Test a GlassController Action which Returns a View Taking a Model 如何对返回 JsonResult 的 Action 方法进行单元测试? - How to unit test an Action method which returns JsonResult? 单元测试任务<IActionResult> - Unit Test Task<IActionResult> 如何对返回Action的方法进行单元测试 <AuthenticationOptions> ? - How to unit test a method that returns Action<AuthenticationOptions>? 如何对依赖于c#中的身份验证的MVC控制器操作进行单元测试? - How to unit-test an MVC controller action which depends on authentication in c#? 如何对返回函数的字典进行单元测试? - How to unit test a dictionary which returns a func? 如何对MVC Controller动作进行单元测试,以调用与Controller关联的服务 - How to unit test MVC Controller Action that calls a service associated with the Controller 如何为返回数组的函数创建单元测试 - How to create unit test for function that returns an array 我如何单元测试返回PartialViewResult的MVC Action? - How can I unit test an MVC Action that returns a PartialViewResult?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM