【问题标题】:ASP.NET Core unit test throws Null Exception when testing controller problem responseASP.NET Core 单元测试在测试控制器问题响应时抛出 Null Exception
【发布时间】:2020-07-14 16:22:49
【问题描述】:

我正在为我的项目创建基本的单元测试。出于某种原因,在测试我收到ControllerBase.Problem(String, String, Nullable<Int32>, String, String) 响应时,我不断收到 NullReferenceException。我确定问题出在与未实际运行的控制器之间存在差异,因为它在控制器运行时似乎表现得非常好。

控制器:

        [HttpGet("{id}")]
        [Produces("application/json")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        public IActionResult GetPatient([GuidNotEmpty] Guid id)
        {
            Patient patient = null;

            patient = _patientDbService.FindPatient(id);
            if (patient == null) {
                return Problem("Patient not found.", string.Empty, StatusCodes.Status404NotFound,
                    "An error occurred.", "https://tools.ietf.org/html/rfc7231#section-6.5.1");
            }

            return Ok(patient);
        }

测试:

        [Fact]
        public void TestGetPatientFromIdPatientNotFound()

        {
            // Act
            IActionResult result = _patientController.GetPatient(Guid.NewGuid());

            // Assert
            Assert.IsType<ObjectResult>(result);
            Assert.NotNull(((ObjectResult)result).Value);
            Assert.IsType<ProblemDetails>(((ObjectResult)result).Value);
            Assert.Equal(((ObjectResult)result).StatusCode, StatusCodes.Status404NotFound);
        }

结果:

X PatientServiceTest.PatientServiceUnitTest.TestGetPatientFromIdPatientNotFound [1ms]
Error Message:
   System.NullReferenceException : Object reference not set to an instance of an object.
Stack Trace:
   at Microsoft.AspNetCore.Mvc.ControllerBase.Problem(String detail, String instance, Nullable`1 statusCode, String title, String type)
   at PatientService.Controllers.PatientController.GetPatient(Guid id) in /home/surafel/coding/microservices-dev/c#/PatientService/Controllers/PatientController.cs:line 43
   at PatientServiceTest.PatientServiceUnitTest.TestGetPatientFromIdPatientNotFound() in /home/surafel/coding/microservices-dev/c#/PatientServiceTest/PatientServiceUnitTest.cs:line 69

【问题讨论】:

  • 你是如何初始化_patientController的?你如何指定它的依赖关系? ControllerBase 依赖于各种框架级服务。
  • 我对此了解不多。但如果我是你,我会 1. 尝试调试测试并检查变量 2. 删除问题陈述的所有参数或将事件更改为 Ok 可能无法解决问题,但可以帮助跟踪问题。 @AluanHaddad 上面提到的测试控制器有点困难,它依赖于很多其他的东西
  • 我正在指定它的依赖项。我嘲笑了我使用的数据库服务,直到现在我还没有遇到任何问题。我曾经测试过BadRequestObjectResult 而不仅仅是ObjectResult
  • 解决此问题的标准方法是什么?我想对我的错误有一个一致的结构。将Problem 调用封装在try-catch 循环中并创建我自己的ProblemDetails 是一个合适的解决方案吗?

标签: c# unit-testing asp.net-core .net-core asp.net-core-webapi


【解决方案1】:

正如 Aluan Haddad 在 cmets 中指出的那样,Problem() 调用 ProblemDetailsFactory 来创建由服务管理器提供的 ProblemDetails 对象。服务管理器仅在应用程序运行时起作用:https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/ControllerBase.cs#L194

可以设置ControllerBase.ProblemDetailsFactory 变量,因此我创建了一个模拟ProblemDetailsFactory 实例并将控制器工厂设置为我的模拟实例。这似乎使它起作用。

模拟:

    public class MockProblemDetailsFactory : ProblemDetailsFactory
    {
        public MockProblemDetailsFactory()
        {
        }

        public override ProblemDetails CreateProblemDetails(HttpContext httpContext,
            int? statusCode = default, string title = default,
            string type = default, string detail = default, string instance = default)
        {
            return new ProblemDetails() {
                Detail = detail,
                Instance = instance,
                Status = statusCode,
                Title = title,
                Type = type,
            };
        }

        public override ValidationProblemDetails CreateValidationProblemDetails(HttpContext httpContext,
            ModelStateDictionary modelStateDictionary, int? statusCode = default,
            string title = default, string type = default, string detail = default,
            string instance = default)
        {
            return new ValidationProblemDetails(new Dictionary<string, string[]>()) {
                Detail = detail,
                Instance = instance,
                Status = statusCode,
                Title = title,
                Type = type,
            };
        }
    }

我在此单元测试的设置中添加了这一行,它解决了问题。

_patientController.ProblemDetailsFactory = new MockProblemDetailsFactory();

【讨论】:

  • 这很实用,但从人体工程学的角度来看确实令人失望
猜你喜欢
  • 2019-03-16
  • 1970-01-01
  • 2018-05-01
  • 2019-07-28
  • 2013-05-28
  • 2017-03-22
  • 2011-08-22
  • 2017-12-12
  • 1970-01-01
相关资源
最近更新 更多