首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何使用ActionResult<T>进行单元测试?

如何使用ActionResult<T>进行单元测试?
EN

Stack Overflow用户
提问于 2018-07-24 09:10:14
回答 5查看 19.5K关注 0票数 39

我有一个xUnit测试,如下所示:

[Fact]
public async void GetLocationsCountAsync_WhenCalled_ReturnsLocationsCount()
{
    _locationsService.Setup(s => s.GetLocationsCountAsync("123")).ReturnsAsync(10);
    var controller = new LocationsController(_locationsService.Object, null)
    {
        ControllerContext = { HttpContext = SetupHttpContext().Object }
    };
    var actionResult = await controller.GetLocationsCountAsync();
    actionResult.Value.Should().Be(10);
    VerifyAll();
}

来源是

/// <summary>
/// Get the current number of locations for a user.
/// </summary>
/// <returns>A <see cref="int"></see>.</returns>
/// <response code="200">The current number of locations.</response>
[HttpGet]
[Route("count")]
public async Task<ActionResult<int>> GetLocationsCountAsync()
{
    return Ok(await _locations.GetLocationsCountAsync(User.APropertyOfTheUser()));
}

结果的值是null,导致我的测试失败,但是如果您查看ActionResult.Result.Value (一个内部属性),它包含预期的解析后的值。

请参见下面的调试器屏幕截图。

如何在单元测试中填充actionResult.Value?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2018-07-24 10:13:51

在运行时,由于隐式转换,测试中的原始代码仍然有效。

但是根据提供的调试器图像,看起来测试是在错误的结果属性上断言的。

因此,虽然更改测试下的方法可以使测试通过,但无论以哪种方式运行,它都可以正常工作

ActioResult<TValue>有两个属性,这两个属性的设置取决于使用它的操作返回的内容。

/// <summary>
/// Gets the <see cref="ActionResult"/>.
/// </summary>
public ActionResult Result { get; }

/// <summary>
/// Gets the value.
/// </summary>
public TValue Value { get; }

Source

因此,当控制器操作使用Ok()返回时,它将通过隐式转换设置操作结果的ActionResult<int>.Result属性。

public static implicit operator ActionResult<TValue>(ActionResult result)
{
    return new ActionResult<TValue>(result);
}

但测试断言了Value属性(请参阅OP中的图像),在本例中未设置该属性。

无需修改被测代码以满足测试要求,它就可以访问Result属性并对该值进行断言

[Fact]
public async Task GetLocationsCountAsync_WhenCalled_ReturnsLocationsCount() {
    //Arrange
    _locationsService
        .Setup(_ => _.GetLocationsCountAsync(It.IsAny<string>()))
        .ReturnsAsync(10);
    var controller = new LocationsController(_locationsService.Object, null) {
        ControllerContext = { HttpContext = SetupHttpContext().Object }
    };

    //Act
    var actionResult = await controller.GetLocationsCountAsync();

    //Assert
    var result = actionResult.Result as OkObjectResult;
    result.Should().NotBeNull();
    result.Value.Should().Be(10);

    VerifyAll();
}
票数 33
EN

Stack Overflow用户

发布于 2019-06-23 23:55:14

问题是把它包装在Ok中。如果返回对象本身,则会正确填充Value

如果您查看Microsoft's examples in the docs,您会发现它们只对NotFound等非默认响应使用控制器方法

[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById(int id)
{
    if (!_repository.TryGetProduct(id, out var product))
    {
        return NotFound();
    }

    return product;
}
票数 17
EN

Stack Overflow用户

发布于 2020-08-02 23:11:43

问题出在ActionResult<T>令人困惑的接口上,这个接口从来没有设计成供我们人类使用。正如在其他答案中所述,ActionResult<T>设置了ResultValue属性,但不能同时设置这两个属性。当您返回一个OkObjectResult时,框架会填充Result属性。当您返回一个对象时,框架会填充Value属性。

我为我的测试库创建了以下简单的帮助器,以帮助我在使用OkObjectResult Ok()或其他从ObjectResult继承的结果时测试返回值

private static T GetObjectResultContent<T>(ActionResult<T> result)
{
    return (T) ((ObjectResult) result.Result).Value;
}

这使我可以执行以下操作:

var actionResult = await controller.Get("foobar");
Assert.IsType<OkObjectResult>(actionResult.Result);
var resultObject = GetObjectResultContent<ObjectType>(actionResult);
Assert.Equal("foobar", resultObject.Id);
票数 15
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51489111

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档