XUnit тестирование, проверка исключения

Подскажите как добиться, чтобы не падал тест при проверке на исключение ObjectNotFoundError

Есть сервис, который я мокаю и в настройке пытаюсь добиться симуляции выброса исключения

public async Task GetRequestWithNoSucccessNotFound()
{
    var mockService = new Mock<ICompanyService>();
    var mockLogger = new Mock<ILogger<CompaniesController>>();
    mockService.Setup(service => service.GetCompany(It.IsAny<int>())).Throws<ObjectNotFoundException>();
    var controller = new CompaniesController(mockLogger.Object, mockService.Object);

    var result = await controller.Get(9999);

    var viewResult = Assert.IsType<RequestResponseModel<CompanyViewModel>>(result);
    Assert.False(result.IsSuccess);
}

Метод сервиса выглядит так

public async Task<CompanyViewModel> GetCompany(int companyId)
{
    var company = await contextDb.Companies.AsNoTracking().Where(p => p.Id == companyId).SelectCompany().FirstOrDefaultAsync();
    if (company == null)
    {
        throw new ObjectNotFoundException();
    }
    return company;
}

При таком положении вещей, тест падает с ошибкой

System.NullReferenceException : Object reference not set to an instance of an object.

Как мне добиться, чтобы фейковый объект не ронял тест, а выкидывал исключение, которое обрабатывается в контроллере?

UPD

GET

public async Task<RequestResponseModel<CompanyViewModel>> Get(int id)
{
    try
    {
        var company = await CompanyService.GetCompany(id);
        return RequestResponseModel<CompanyViewModel>.CreateSuccessObject(company);
    }
    catch (Exception e)
    {
        LogError(e);
        this.SetErrorResponseStatusCode(e);
        return RequestResponseModel<CompanyViewModel>.CreateRequestResponseErrorObject();
    }
}

SetErrorResponseStatusCode

public static void SetErrorResponseStatusCode(this ControllerBase controller, Exception exception)
    {
        // set http status based on type of exception
        if (exception is NotImplementedException)
        {
            controller.Response.StatusCode = (int)HttpStatusCode.NotImplemented;
        }
        else if (exception is ObjectNotFoundException)
        {
            controller.Response.StatusCode = (int)HttpStatusCode.NotFound; // вот здесь падает
        }
        else
        {
            controller.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        }
    }

Хочу отметить, что падает только тест. При обращении к API все корректно работает.


Ответы (1 шт):

Автор решения: Alexander Petrov

Свойство Response равно null, потому что в этот момент ещё не задано.

Следует возвращать нужный ActionResult вместо задания StatusCode.

public static IActionResult ErrorResponseStatusCode(this ControllerBase controller, Exception exception)
{
    if (exception is NotImplementedException)
    {
        return new StatusCodeResult(StatusCodes.Status501NotImplemented);
    }
    else if (exception is ObjectNotFoundException)
    {
        return controller.NotFound();
    }
    else
    {
        return new StatusCodeResult(StatusCodes.Status500InternalServerError);
    }
}

Приставку Set я убрал, т. к. теперь этот метод не задаёт значение, а возвращает.

Соответственно меняем метод Get:

public async IActionResult Get(int id)
{
    try
    {
        ...
    }
    catch (Exception e)
    {
        LogError(e);
        return this.ErrorResponseStatusCode(e);
    }
}

Применяем pattern matching и expression body:

public static IActionResult ErrorResponseStatusCode(this ControllerBase controller, Exception exception)
    => exception switch
    {
        NotImplementedException => new StatusCodeResult(StatusCodes.Status501NotImplemented),
        ObjectNotFoundException => controller.NotFound(),
        _ => new StatusCodeResult(StatusCodes.Status500InternalServerError)
    };
→ Ссылка