简体   繁体   English

如何从任务中获取值<IActionResult>通过用于单元测试的 API 返回

[英]How to get the Values from a Task<IActionResult> returned through an API for Unit Testing

I have created an API using ASP.NET MVC Core v2.1.我使用 ASP.NET MVC Core v2.1 创建了一个 API。 One of my HttpGet methods is set up as follows:我的HttpGet方法之一设置如下:

public async Task<IActionResult> GetConfiguration([FromRoute] int? id)
{
    try
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        ..... // Some code here

        return Ok(configuration);
    }
    catch (Exception ex)
    {
        ... // Some code here
    }
}

When unit testing this I can check that Ok was the response, but I really need to see the values of the configuration.在进行单元测试时,我可以检查 Ok 是响应,但我确实需要查看配置的值。 I don't seem to be able to get this to work with the following:我似乎无法让它与以下一起工作:

[TestMethod] 
public void ConfigurationSearchGetTest()
{
    var context = GetContextWithData();
    var controller = new ConfigurationSearchController(context);
    var items = context.Configurations.Count();
    var actionResult = controller.GetConfiguration(12);

    Assert.IsTrue(true);
    context.Dispose();
}

At runtime, I can check that actionResult has certain values that I am unable to code for.在运行时,我可以检查actionResult是否具有某些我无法编​​码的值。 Is there something I am doing wrong?有什么我做错了吗? Or am I just thinking about this wrong?还是我只是在想这个错误? I would like to be able to do:我希望能够做到:

Assert.AreEqual(12, actionResult.Values.ConfigurationId);

You can get tested controller without changing returned type.您可以在不更改返回类型的情况下获得经过测试的控制器。
IActionResult is base type for all others. IActionResult是所有其他人的基本类型。
Cast result into expected type and compare returned value with expected.将结果转换为预期类型并将返回值与预期进行比较。

Since you are testing asynchronous method, make test method asynchronous as well.由于您正在测试异步方法,因此也要使测试方法异步。

[TestMethod] 
public async Task ConfigurationSearchGetTest()
{
    using (var context = GetContextWithData())
    {
        var controller = new ConfigurationSearchController(context);
        var items = context.Configurations.Count();

        var actionResult = await controller.GetConfiguration(12);

        var okResult = actionResult as OkObjectResult;
        var actualConfiguration = okResult.Value as Configuration;

        // Now you can compare with expected values
        actualConfuguration.Should().BeEquivalentTo(expected);
    }
}

Good practice would suggest that you don't have a lot of code in your controller actions to test and the bulk of logic is in decoupled objects elsewhere that are much easier to test.好的做法是建议您在控制器操作中没有大量代码需要测试,并且大部分逻辑位于其他更容易测试的解耦对象中。 Having said that, if you still want to test your controllers then you need to make your test async and await the calls.话虽如此,如果您仍然想测试您的控制器,那么您需要使您的测试async并等待调用。

One of the problems you will have is that you are using IActionResult as it allows you to return BadRequest(...) and Ok(...) .您将遇到的问题之一是您正在使用IActionResult因为它允许您返回BadRequest(...)Ok(...) However, since you are using ASP.NET MVC Core 2.1, you may want to start using the new ActionResult<T> type instead.但是,由于您使用的是 ASP.NET MVC Core 2.1,您可能希望开始使用新的ActionResult<T>类型。 This should help with your testing because you can now get direct access to the strongly typed return value.这应该有助于您的测试,因为您现在可以直接访问强类型返回值。 For example:例如:

//Assuming your return type is `Configuration`
public async Task<ActionResult<Configuration>> GetConfiguration([FromRoute] int? id)
{
    try
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        ..... // Some code here

        // Note we are now returning the object directly, there is an implicit conversion 
        // done for you
        return configuration;
    }
    catch (Exception ex)
    {
        ... // Some code here
    }
}

Note we are now returning the object directly as there is an implicit conversion from Foo to ActionResult<Foo>请注意,我们现在直接返回对象,因为存在FooActionResult<Foo>隐式转换

Now your test can look like this:现在你的测试看起来像这样:

[TestMethod] 
public async Task ConfigurationSearchGetTest()
{
    var context = GetContextWithData();
    var controller = new ConfigurationSearchController(context);
    var items = context.Configurations.Count();

    // We now await the call
    var actionResult = await controller.GetConfiguration(12);

    // And the value we want is now a property of the return
    var configuration = actionResult.Value;

    Assert.IsTrue(true);
    context.Dispose();
}

As my reputation does not allow me to comment on @DavidG answer which goes in the right direction, I will put a sample on how to get the value inside Task<IActionResult> .由于我的声誉不允许我评论 @DavidG 的答案是正确的,我将放一个关于如何在Task<IActionResult>获取值的示例。

As @ Christopher J. Reynolds pointed out, actionResult.Value can be seen at runtime but not at compilation .正如@ Christopher J. Reynolds 指出的那样, actionResult.Value可以在运行时看到,但不能在编译看到。

So, I'll show a basic test in which get the Values :因此,我将展示一个获取Values的基本测试:

[TestMethod]
public async Task Get_ReturnsAnArea()
{
    // Arrange
    string areaId = "SomeArea";
    Area expectedArea = new Area() { ObjectId = areaId, AreaNameEn = "TestArea" };

    var restClient = new Mock<IRestClient>();
    restClient.Setup(client => client.GetAsync<Area>(It.IsAny<string>(), false)).ReturnsAsync(expectedArea);

    var controller = new AreasController(restClient.Object);

    //// Act

    // We now await the call
    IActionResult actionResult = await controller.Get(areaId);

    // We cast it to the expected response type
    OkObjectResult okResult = actionResult as OkObjectResult;

    // Assert

    Assert.IsNotNull(okResult);
    Assert.AreEqual(200, okResult.StatusCode);

    Assert.AreEqual(expectedArea, okResult.Value);

   // We cast Value to the expected type
    Area actualArea = okResult.Value as Area;
    Assert.IsTrue(expectedArea.AreaNameEn.Equals(actualArea.AreaNameEn));
}

For sure this could be improved but I just wanted to show you a simple way to get it.当然,这可以改进,但我只是想向您展示一个简单的方法来获得它。

I hope it helps.我希望它有帮助。

You need to await the call to GetConfiguration to get the IActionResult object back as follows:您需要等待对 GetConfiguration 的调用以获取 IActionResult 对象,如下所示:

var actionResult = await controller.GetConfiguration(12);

To do this you need to change the signature of your test method to be async as well.为此,您还需要将测试方法的签名更改为异步。 So change this:所以改变这个:

public void ConfigurationSearchGetTest()

To this:对此:

public async Task ConfigurationSearchGetTest()

If you need fast solution, use JsonConvert.SerializeObject() and after that JsonConvert.DeserializeObject() then you will get Object with values.如果您需要快速解决方案,请使用 JsonConvert.SerializeObject() ,然后使用 JsonConvert.DeserializeObject() 然后您将获得带有值的对象。

 [TestMethod] 
    public async Task ConfigurationSearchGetTest()
    {
        using (var context = GetContextWithData())
        {
            var controller = new ConfigurationSearchController(context);
            var items = context.Configurations.Count();
    
            var actionResult = await controller.GetConfiguration(12);
    
            var okResult = actionResult as OkObjectResult;
            var actualConfiguration = okResult.Value ;
  //
  //IMPORTANT ONLY BELOW two lines  need.
  //

var actualConfigurationJStr=JsonConvert.SerializeObject( okResult.Value );
var hereObjectWithStrongType=JsonConvert.DeserializeObject<Configuration>(actualConfigurationJStr);

    
            // Now you can compare with expected values
            actualConfuguration.Should().BeEquivalentTo(expected);
        }
    }

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM