3

I am working on the Unit Testing in Asp.Net Mvc Web Api. I have 2 projects

1: Catalog.Api - This contains all the controllers

2: Catalog.UnitTests - This contains the Unit Test for controllers

All Controllers are Inherit with "ApiController" and every controller has custom filter [AuthenticationFilter]. Here is my values controller.

    [AuthenticationFilter]
    public class ValuesController : ApiController
    {
        // GET api/values
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/values/5
        public string Get(int id)
        {
            return "value";
        }

        // POST api/values
        public void Post([FromBody]string value)
        {
        }

        // PUT api/values/5
        public void Put(int id, [FromBody]string value)
        {
        }

        // DELETE api/values/5
        public void Delete(int id)
        {
        }
    }

And my custom is check the authorization token. Here it is

public class AuthenticationFilter: AuthorizationFilterAttribute
    {
        public override void OnAuthorization(HttpActionContext actionContext)

        {
            var request = actionContext.Request;
            var authorization = request.Headers.Authorization;

            if (authorization == null || authorization.Scheme != "Bearer")
            { 
                ShowAuthenticationError(actionContext, "Authorization required");
                return;
            }

            if (string.IsNullOrEmpty(authorization.Parameter))
            {
                ShowAuthenticationError(actionContext, "Missing Jwt Token");
                return;
            }

            var token = authorization.Parameter;

            var principal = AuthenticateToken(token);
            if (principal == null)
            { 
                ShowAuthenticationError(actionContext, "Invalid token");
                return;
            }
            base.OnAuthorization(actionContext);
        }
        private static void ShowAuthenticationError(HttpActionContext filterContext, string message)
        {
            var responseDTO = new ResponseDTO() { Code = 401, Message = message };
            filterContext.Response =
            filterContext.Request.CreateResponse(HttpStatusCode.Unauthorized, responseDTO);
        }
    }
    public class ResponseDTO
    {
        public int Code { get; set; }
        public string Message { get; set; }
    }

Now in the Unit Test project i have a class and unit test method.

        [TestMethod]
        public void CheckFilter()
        {
            try
            {
                var controller = new ValuesController();
                var controllerContext = new HttpControllerContext();
                var request = new HttpRequestMessage();
                request.Headers.Add("Authorization", "bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6InVhbGkiLCJlbWFpbCI6InVhbGlAaW5yZWFjaGNlLmNvbSIsIm5iZiI6MTU2NDY0NjIyMSwiZXhwI");

                controllerContext.Request = request;
                controller.ControllerContext = controllerContext;

                var result = controller.Get();
                Assert.IsTrue(result.Any());

            }
            catch (Exception ex)
            {
                Assert.Fail();
            }
        }

I am calling my controller by adding reference of API project into my unit test project. So all controllers are available in the unit test project.

Issue is that when i call the values controller it always return the data. And when i remove the request and header so it is also returning the data but in that case that will be unauthorized.

I think my custom filter is not calling. How should that would be called and authenticate the user.

1 Answer 1

1

I check your question and configure that issue it is basically you are calling the controller directly. Basically controller is a class and when you are calling that it is behaving like a simple class and call the method and send back the result. It is simple and clear

But in your situation you have project for your api so can do this.

[TestMethod]
public void CheckFilter()
{
    try
    {
        var config = new HttpConfiguration();
        // This is the resgister method which is written in you Api project. That code is after this method this method because i did the same thing to call my controller.
        Catalog.Api.WebApiConfig.Register(config);
        using (var server = new HttpServer(config))
        {
            var client = new HttpClient(server);

            string url = "http://localhost:PortNumberOfProject/api/values";

            var request = new HttpRequestMessage
            {
                RequestUri = new Uri(url),
                Method = HttpMethod.Get
            };
            request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "Your Token");

            var response = await client.SendAsync(request);
            Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
        }
    }
    catch (Exception ex)
    {
        Assert.Fail();
    }
}

Here is the WebApi Register method of Api project which is used to register the Api and Routes.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();

        var cors = new EnableCorsAttribute("*", "*", "*");
        config.EnableCors(cors);

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

Here is your controller as it is. And now debug your test and add a break point in your [AuthenticationFilter] and OnAuthorization method.

[AuthenticationFilter]
public class ValuesController : ApiController
{
    // GET api/values
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

This means that you need to host the asp.net application in order to test it. Just doesn't seem like a good approach. You can only check for error codes. You can't mock a service and check it's values with this.

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.