Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UnitTest ApiController with ExceptionFilterAttribute

i'm trying to UnitTest my controller where an exception is catch by the ExceptionFilterAttribute and launched back as a HttpResponseException.

Controller

[ExceptionFilters] //ExceptionFilterAttribute
public class EleveController : ApiController
{ 
  private IGpiRepository _gpiRepository;

  public EleveController(IGpiRepository gpiRepository)
  {
     _gpiRepository = gpiRepository;
  }

  [HttpGet]
  [Route("{fiche:int}/grouperepere")]
  public GroupeRepere GroupeRepere(int fiche) //This What Im trying to test
  {
     GpiService service = new GpiService(_gpiRepository);
     return service.Get(fiche); //Throw an ArgumentNullException when fiche == 0
  }
}

ExceptionFilter

public class ExceptionFilters : ExceptionFilterAttribute
{
  public override void OnException(HttpActionExecutedContext context)
  {
     if (context.Exception is NotImplementedException)
     {
        context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
     }
     else if (context.Exception is ArgumentNullException)
     {
        context.Response = new HttpResponseMessage(HttpStatusCode.BadRequest)
        {
           Content = new StringContent(string.Format("Argument \"{0}\" is null or invalid", ((ArgumentNullException)context.Exception).ParamName)),
           ReasonPhrase = "Argument null or invalid"
        };
     }
  }

and this is my test:

  private IGpiRepository _gpiRepository;
  private Mock<ICallApi> _callApi;
  private EleveController _controller;

  [TestInitialize]
  public void Initialize()
  {
     _callApi = new Mock<ICallApi>();
     _gpiRepository = new GpiRepository(_callApi.Object);
     _controller = new EleveController(_gpiRepository);
  }

  [TestMethod]
  public void EleveController__GroupeRepere_WithBadFiche_400BadRequest()
  {
     string noGroupe = "111";
     int fiche = 0;
     try
     {
        GroupeRepere gp = _controller.GroupeRepere(fiche);

        Assert.Fail();
     }
     catch (Exception e)
     {
        Assert.IsTrue(e is HttpResponseException); // not working --> ArgumentNullException
     }
  }

The problem is that e still is an ArgumentNullException. When i go on debug, it doesn't even reach the ExceptionFilter class

Am i missing something? Thanks.

like image 768
LD Robillard Avatar asked Jul 17 '15 15:07

LD Robillard


People also ask

How to unit test actionfilter?

To unit test an action filter, you have to pass in an action filter context object (which requires a lot of setup). Action filter methods are void, so you have to verify the behavior by inspecting the context object (or dependencies, like a logger, if you are injecting those).

Is it possible to unit test Web API?

You can either create a unit test project when creating your application or add a unit test project to an existing application. This tutorial shows both methods for creating a unit test project. To follow this tutorial, you can use either approach.

What are unit testing controllers in web API?

In this article, you will learn about unit testing controllers in Web API. TDD (Test-driven development) is a developmental approach in which TFD (Test-First Development) is there, and where we write a test before writing a code for the production. TDD is also supported by both MVC and Web API.

How to apply the custom exception filter to all web API controllers?

Finally, in order to apply this at the global level, in other words for all Web API controllers, we will do the following: Add to the filters collection in a global configuration. CRUDWebAPI.MyCustomExceptionFilter ctrlr = new CRUDWebAPI.MyCustomExceptionFilter (); GlobalConfiguration.Configuration.Filters (ctrlr);

How do I add unit tests to an express API?

It talks about refactoring express api into a RESTful app. To add unit tests, we first move our request specs to request folder, and create models and controllers folders to hold our unit tests. Let’s first test our post and user models.

How to pass test data when unit testing a controller?

Notice that the controller includes a constructor that takes as a parameter a list of Product objects. This constructor enables you to pass test data when unit testing. The controller also includes two async methods to illustrate unit testing asynchronous methods.


1 Answers

Your test is directly against the controller. ExceptionFilterAttribute depends on a server.(remember: attributes are Metadata)

The way to test the behavior is to use IIS server or SelfHost Server, then raise the server in your test class and send the request:

[TestInitialize]
public void Initialize()
{
    _callApi = new Mock<ICallApi>();
    _gpiRepository = new GpiRepository(_callApi.Object);

   //initialize your server
   //set _gpiRepository as a dependency and etc..
}
[TestMethod]
public void EleveController__GroupeRepere_WithBadFiche_400BadRequest()
{
    //configure the request
    var result = client.ExecuteAsGet<GroupeRepere>(<your request>);

    Assert.AreEqual(HttpStatusCode.BadRequest,result.StatusCode);
}

In my opinion you shouldn't error code unless your controller is apart of public Api.(the reason is simple this kind of tests are very simple to break, thay are slow and thay use expensive resources) if your controller is a part public Api you should test it through your Acceptance tests, then you verify that nothing override the expected behavior.

If you still want to test this behavior then i'd like to offer you an alternative way to test it:

  1. Create UT against ExceptionFilters.
  2. Create a UT which verifies that the method has ExceptionFilters attribute

For example:

[TestMethod]
public void GroupeRepere_HasExceptionFiltersAttribute()
 {
    var attribute = typeof (UnitTest2).GetMethod("GroupeRepere").GetCustomAttributes(true);

    foreach (var att in attribute)
    {
        if(att.GetType() is typeof(ExceptionFilters))
        {
            return;
        }
    }
    Assert.Fail();
}

Pros: it' fast, not so easy to break, it doesn't use expensive reasorces.

Cons: In production some setting could override the expected behavior.

like image 116
Old Fox Avatar answered Oct 17 '22 04:10

Old Fox