如何为Core api控制器测试ModelState的自定义属性方法自动验证?

时间:2019-06-17 12:18:59

标签: c# asp.net-core .net-core modelstate

我想测试给定MyModelDTO值的控制器方法。

这是我的控制器Post方法(简化):

[HttpPost]
public ActionResult Post([FromBody] MyModelDTO itemDTO)
{
    ModelState.Remove($"{nameof(itemDTO)}.{nameof(itemDTO.Id)}");
    if (!ModelState.IsValid)
    {
        return BadRequest();
    }
    //rest of code
}

我的MyModelDTO班:

public class MyModelDTO
{
    [IsNotEmpty(ErrorMessage = "Guid Id Is Empty")]
    public Guid Id { get; set; }
}

我的自定义ValidationAttribute

public class IsNotEmptyAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        if (value == null) return false;

        var valueType = value.GetType();
        var emptyField = valueType.GetField("Empty");

        if (emptyField == null) return true;

        var emptyValue = emptyField.GetValue(null);

        return !value.Equals(emptyValue);
    }
}

我的问题是如何测试ModelState的自定义属性的自动验证

这是我尝试过的:

[Test] public void Post_WhenCalled_ShouldReturnPostResult()
{
    using (var mock = AutoMock.GetLoose())
    {
        //Arrange
        var controller = mock.Create<MyController>();

        //Act
        ActionResult actionResult = controller.Post(new MyModelDTO());

        //Assert... 
     }
}

单元测试可以正常工作(控制器应该使用没有MyModelDTO的参数Id来工作),但是看起来它并没有真正嘲笑ModelState的自动验证过程。我怎么知道因为当我尝试使用缺少Id属性的正文的邮递员时,会出现"Guid Id Is Empty"消息。它甚至不会在断点处停止。

2 个答案:

答案 0 :(得分:2)

您有两种选择:可以对其进行单元测试,也可以对其进行集成测试。现在的问题是,您正在尝试将两种方法混合使用。

要进行适当的单元测试,您只需实例化属性本身并将数据传递到IsValid,以确保其正确返回true / false。不需要控制器或其他任何东西。本质上,您只是在测试执行验证的实际方法,以确保其正确执行。

如果要完全测试它,以确保它在从管道内部的请求传递数据的实际情况下可以正常工作,则需要进行集成测试,但这需要测试服务器的用户,并且实际上使用测试客户端(只是一个HttpClient实例)发出真正的请求。

仅更新控制器并调用诸如方法之类的操作是不够的。除其他差异外,它不涉及模型绑定器,因此也不涉及任何验证机制。简而言之,您的属性无法使用,因为甚至从未调用过它。

答案 1 :(得分:2)

作为模型绑定过程的一部分,模型验证在控制器外部调用。这意味着您不能真正在控制器单元测试中对其进行测试。相反,在测试控制器时,如果您想基于模型状态验证控制器的行为,则基本上已经必须模拟模型状态。

您基本上可以在这里做两件事:如果您只想测试验证逻辑,那么最好的方法就是直接调用ValidationAttribute。因此,您无需测试控制器,但可以测试属性。

您可以简单地实例化属性,然后运行Validate方法以测试其行为。只需传递您要验证的对象的实例,就可以验证它引发的异常。

另一种解决方案是执行完整的integration test。这样,您就无需对控制器进行单元测试,而是对整个请求管道(包括控制器和模型验证)进行测试。对于特定情况,这是确保所有操作都能端到端的最佳方法。