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
    }
Help123
  • 1,511
  • 2
  • 28
  • 46
  • Possible duplicate of [Assert an Exception using XUnit](https://stackoverflow.com/questions/45017295/assert-an-exception-using-xunit) – maracuja-juice Oct 12 '18 at 03:04

1 Answers1

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.

Win
  • 61,100
  • 13
  • 102
  • 181
  • 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? – Help123 Oct 11 '18 at 22:57
  • 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. – Win Oct 12 '18 at 16:04