简体   繁体   中英

How can I unit test controller together with a filter (ASP.NET MVC with autofac)

so I am writing a high-level unit test in ASP.NET MVC 4 using autofac.

So I have a sample controller:

    public class SomeController
    {        
        [SomeFilter]
        public ActionResult SomeAction()
        {
            SomeCode();
        }
    }

And I can write a sample test:

    [Test]
    public void Test()
    {
        var controller = new SomeController();
        var result = controller.SomeAction();
        // Asserts go here
    }

That all works great, provided I fake out all external dependencies. However, there is also some code that is attached via filter attribute that I would like to run (it is important to this test, and I do not want to just test it in isolation).

This code would be executed when run within the application, but it would not be executed if run within the test. It does not matter if I new up controller manually, or retrieve it using DependencyResolver as:

var someController = DependencyResolver.Current.GetService<SomeController>();

This is obviously because during normal run-time the framework creates and attaches those filters properly.

So the question is - how could I duplicate this behavior in the test and have those action filters executed?

Good Morning Sebastian,

For reasons beyond posting here I had to write unit tests to utilize the action filter along with the web api controller end point.

Of course as you observed and I found out as well the action filters do not fire during a unit test.

I hacked my way to this solution but am definitely open to further education.

In my unit test I am using a fluent builder pattern but in short spin up your controller. (BTW if you want more info on a fluent builder pattern just post a comment and I'll pass along links...that or Google it.)

//we need to setup some context stuff that you'll notice is passed 
//in to the builder WithControllerContext...and then the controller is 
//used to get the ActionFilter primed

//0. Setup WebApi Context
var config = new HttpConfiguration();
var route = config.Routes.MapHttpRoute("DefaultSlot", "cars/_services/addPickup");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "AddPickup" } });


var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:24244/cars/_services/addPickup");
request.RequestUri = new Uri("http://localhost:24244/cars/_services/addPickup");
List<string> x = new List<string>() { MediaType.CarsV2.GetDescription() };
request.Headers.Add("Accept", x);


 ....obviously 1 & 2 I skipped as they aren't relevant


//3. controller
PickupController controller = new PickupControllerBuilder()
//.MoqReqHlpr_IsAny_CreateResp(HttpStatusCode.OK) //3a. setup return message
.MoqCarRepoAny_ReturnsSampleDataCar(new Guid("000...0011")) //3b. car object
.MoqFulfillRepoAnyCar_IsAny_IsQuickShop(true) //3c. fulfillment repository
.MoqCartRepoAnyCar_IsAny_UpdateCar(true) //3d. car repository
.WithControllerContext(config, routeData, request)
.WithHttpPropertyKey(lsd);


//HttpActionContextFilter is the name of my ActionFilter so as you can
//see we actually instantiate the ActionFilter then hook it to the
//controller instance
var filter = new HttpActionContextFilter();
filter.OnActionExecuting(controller.ActionContext);

Two things to note that bit me.

  1. make sure you have the correct namespace. I let VS import it via Ctrl-. and of course it imported System.Web.Mvc and I needed System.Web.Http.
  2. You need to hook the overrides you want to fire. So if your action filter has four overrides then you need to hook four times. If you have multiple ActionFilters then you need to instantiate each and hook its overrides.

I included the screenshot thinking a picture would be helpful here.

在此输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM