0

I am trying to unit test a controller that returns an IActionResult but can also throw an exception in certain circumstances. The problem I am running into is I'm not sure how to call it as the Assert throws an error where you cannot convert from IActionResult to Action.

How would I go about testing below statement?

 Assert.Throws<Exception>(await controller.SendEmail(email)); //how to test this

I looked through the Microsoft testing controller documentation and didn't find something relevant. Most examples I see testing exceptions are for things like accessing repositories or services.

I understand I can return a badrequest or redirect to the page with an error message. But is what I am trying to accomplish possible?

My HomeController Method

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> SendEmail(EmailModel emailModel)
        {
            if(!ModelState.IsValid)
            {
                return View("Index",emailModel);
            }

            var response = await _sendEmail.SendEmailMessage(emailModel);

            if (response != System.Net.HttpStatusCode.Accepted)
            {
                throw new Exception("Email failed to send, please try again later");
            }
            else
            {
                TempData["message"] = $"Email has been sent";
            }

            return RedirectToAction("Index");
        }

XUnit HomeControllerTest Constructor for arrange

        private Mock<ISendEmail> mockSendEmail;
        private HomeController controller;

        public HomeControllerShould()
        {
            mockSendEmail = new Mock<ISendEmail>();
            mockSendEmail.Setup(x => x.SendEmailMessage(It.IsAny<EmailModel>())).ReturnsAsync(HttpStatusCode.Accepted);
            controller = new HomeController(mockSendEmail.Object);
        }

XUnit Test for Sending Email

 [Fact]
    public async Task SendEmailActionThrowsExceptionOnEmailFailure()
    {
        mockSendEmail.Setup(x => x.SendEmailMessage(It.IsAny<EmailModel>())).ReturnsAsync(HttpStatusCode.InternalServerError);
        var email = new EmailModel();

        Assert.Throws<Exception>(await controller.SendEmail(email)); //how to test this
    }
1

1 Answer 1

2

Assert.Throws requires a function. You could use ThrowsAsync.

[Fact]
public async Task SendEmailActionThrowsExceptionOnEmailFailure()
{
    mockSendEmail.Setup(x => x.SendEmailMessage(It.IsAny<EmailModel>()))
        .ReturnsAsync(HttpStatusCode.InternalServerError);

    var email = new EmailModel();

     await Assert.ThrowsAsync<Exception>(() => controller.SendEmail(email)); 
}

FYI: We don't normally return HttpStatusCode from service layer such as email service, but I'll let you decide.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you! Was looking at the documentation for throws and saw that it takes delegates. Would I be better off returning a bool on success / failure from the service layer?
SMTP is a relay service, so you won't know right away even if the message could not be delivered. You could just return Boolean or void. Another scenario would be queue those emails and then process them from another microservice.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.