Search code examples
asp.net-corefluent-assertions

Asserting Redirect to Action


When using ASP.Net Core 2 MVC and FluentAssertions.AspNetCore.Mvc, how do I assert that a controller redirected to an action?

For example, given the following controller action:

public IActionResult SomeAction() {
  return RedirectToAction("SomeOtherAction");
}

How would I write a test verifying the redirection?

I'm looking for something like:

[Fact]
public void SomeActionTest() {
  var controller = new SomeController();
  var result = controller.SomeAction();

  result.Should().BeRedirectedToRouteResult().WithAction("SomeOtherAction");
}

...except instead of BeRedirectedToRouteResult().WithAction("SomeOtherAction"), I'm looking to assert something like BeRedirectedToAction("SomeOtherAction").


Solution

  • I personally would do something like the following:

    Creating a static class containing the extension method and assertion class, which has a method called BeRedirectAction and then can be used like the following:

        [Fact]
        public void ActionReturnsView_ExpectedRedirectToError_TypeMismatch()
        {
            var controller = new HomeController();
    
            var result = controller.Index();
    
            result.Should().BeRedirectAction(nameof(HomeController.Error));
        }
    

    Extension Method + Assertion Class

    Example Static class

    public static class ActionResultAssertionExtensions
    {
        public class ActionResultAssertions : ObjectAssertions
        {
            public new IActionResult Subject { get; }
    
            public ActionResultAssertions(IActionResult subject) : base(subject)
            {
                Subject = subject;
            }
    
            [CustomAssertion]
            public void BeRedirectAction(string actionName, string because= null, params object[] becauseArgs)
            {
                var redirectResult = AssertionExtensions.Should(Subject).BeOfType<RedirectToActionResult>().Which;
    
                var actual = redirectResult.ActionName;
                var expected = actionName;
    
                Execute.Assertion.ForCondition(actual == expected)
                    .BecauseOf(because, becauseArgs)
                    .FailWith("Expected {context} to redirect to {0} Action but it is redirecting to {1}", expected, actual);
            }
        }
    
        public static ActionResultAssertions Should(this IActionResult subject)
        {
            return new ActionResultAssertions(subject);
        }
    }
    

    Sample Tests

    Type Mismatch Test

    This is an example failure of when the result is not a redirect:

        [Fact]
        public void ActionReturnsView_ExpectedRedirectToError_TypeMismatch()
        {
            var controller = new HomeController();
    
            var result = controller.Index();
    
            result.Should().BeRedirectAction(nameof(HomeController.Error));
        }
    

    Result:

    Expected type to be Microsoft.AspNetCore.Mvc.RedirectToActionResult, but found Microsoft.AspNetCore.Mvc.ViewResult.
    

    Success Test

    This is an example of a passing test

        [Fact]
        public void ActionRedirectsToError_ExpectedRedirectToError_TestShouldPass()
        {
            var controller = new HomeController();
    
            var result = controller.RedirectToError();
    
            result.Should().BeRedirectAction(nameof(HomeController.Error));
        }
    

    Different Action Test

    This is an example of a test failure when it gets redirected to a different action:

        [Fact]
        public void ActionRedirectsToIndex_ExpectedRedirectToError_TestSHouldFailSayingDifferentActionName()
        {
            var controller = new HomeController();
    
            var result = controller.RedirectToIndex();
    
            result.Should().BeRedirectAction(nameof(HomeController.Error));
        }
    

    Result:

    Expected result to redirect to "Error" Action but it is redirecting to "Index"

    N.B.

    The above does not test for controller/area differences, or any other potential combinations, it just looks at the Action Name.