Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Core unit test throws Null Exception when testing controller problem response

I'm creating basic unit tests for my project. For some reason, I keep getting a NullReferenceException when testing that I get a ControllerBase.Problem(String, String, Nullable<Int32>, String, String) response. I'm sure the problem is a discrepancy from the controller not actually running, as it seems to behave perfectly fine when the controller is running.

Controller:

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

Test:

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

Result:

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
like image 487
CNJMC1 Avatar asked Jul 14 '20 16:07

CNJMC1


1 Answers

As pointed out by Aluan Haddad in the comments, the that Problem() calls ProblemDetailsFactory to create the ProblemDetails objects, which is supplied by the service manager. The service manager only works when the application is running:https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/ControllerBase.cs#L194

The ControllerBase.ProblemDetailsFactory variable can be set, so I created a mock ProblemDetailsFactory instance and set the controllers factory to an instance of my mock. This seems to make it work.

Mock:

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

I added this line in the setup for this unit tests, and it solves the problem.

_patientController.ProblemDetailsFactory = new MockProblemDetailsFactory();
like image 90
CNJMC1 Avatar answered Oct 17 '22 11:10

CNJMC1