Unit testing ASP.NET Web API Controller

Posted by Anuraj on Tuesday, September 3, 2013

.Net ASP.Net ASP.Net MVC Entity Framework Unit Testing Web API

This post is about unit testing Web API controller. As we are using Entity Framework and some Web API related classes, we need to implement dependency injection to unit test Web API controllers.

To manage Entity Framework dependency, I am using a simple Repository pattern. Here is my repository interface.

public interface IRepository
{
    IEnumerable<Employee> ReadAll();
    Employee ReadById(int id);
    int Create(Employee employee);
    int Update(int id, Employee employee);
    int Delete(int id);
}

As this post is more focusing on unit testing, I am not including the implementation of the IRepository interface, it is same as we used in the controller. And I am modified the controller like this, one more constructor added, which accepts IRepository as one parameter.

private readonly IRepository _employeeRepository;
public EmployeeController()
    : this(new EmployeeRepository())
{
//Empty constructor.
}
//For Testability
public EmployeeController(IRepository repository)
{
    _employeeRepository = repository;
}

And the Get methods in the controller is modified like this.

public HttpResponseMessage Get()
{
    return Request.CreateResponse<IEnumerable<Employee>>
                    (HttpStatusCode.OK, _employeeRepository.ReadAll());
}

public HttpResponseMessage Get(int id)
{
    var selectedEmployee = _employeeRepository.ReadById(id);
    if (null != selectedEmployee)
    {
        return Request.CreateResponse<Employee>
            (HttpStatusCode.OK, selectedEmployee);
    }
    else
    {
        return Request.CreateErrorResponse
            (HttpStatusCode.NotFound, "Employee not found");
    }
}

Instead of using Entity Framework directly, interacting to DB via Repository. You can write the unit test like this to verify the Get method.

//ARRANGE
var expected = new List<Employee>(1) { 
    new Employee() { Id = 1, Email = "email", Name = "name", Phone = "123" }
};
Mock<IRepository> mockRepository = new Mock<IRepository>();
mockRepository.Setup(x => x.ReadAll()).Returns(() => expected);
var controller = new EmployeeController(mockRepository.Object);
controller.Request = new HttpRequestMessage()
{
    Properties = { { HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration() } }
};
//ACT
var response = controller.Get();

//ASSERT
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
var actual = response.Content.ReadAsAsync<List<Employee>>().Result;
Assert.AreEqual(expected.Count, actual.Count);

For mocking purposes I am using Moq library. As we are using the Request property to create the HttpResponseMessage, you need to set a default value to the request property, otherwise you will get some null reference exception from the unit test.

Similar to Request property, in the post method we are using the Url property from APIController class, which is an instance of the UrlHelper class. Unlike the Request property, Url property is quite complex to create, so I am using a wrapper class, which will wraps the Url.Link() method. And I created an interface like this.

public interface IUrlHelper
{
    string GetLink(string routeName, object routeValues);
    UrlHelper Url { get; set; }
}

And the implementation is like this.

public class CustomUrlHelper : IUrlHelper
{
    public UrlHelper Url { get; set; }

    public string GetLink(string routeName, object routeValues)
    {
        return Url.Link(routeName, routeValues);
    }
}

To use the IUrlHelper interface, I modified the controller class again like this.

private readonly IRepository _employeeRepository;
private readonly IUrlHelper _urlHelper;

public EmployeeController()
    : this(new EmployeeRepository(), new CustomUrlHelper())
{
    _urlHelper.Url = Url; //In controller empty constructor, you need to set this property.
}
//For Testability
public EmployeeController(IRepository repository, IUrlHelper urlHelper)
{
    _employeeRepository = repository;
    _urlHelper = urlHelper;
}

Both Post and Put method modified to use _urlHelper, instead of the Url property.

public HttpResponseMessage Post(Employee employee)
{
    _employeeRepository.Create(employee);
    var response = Request.CreateResponse<Employee>
        (HttpStatusCode.Created, employee);
    response.Headers.Location = 
        new Uri(_urlHelper.GetLink("DefaultApi", new { id = employee.Id }));
    return response;
}

Here is the test method for verifying the Post method in the controller.

//ARRANGE
var isCreatedInvokedInRepository = false;
var employee = new Employee()
{
    Id = 1,
    Email = "email",
    Name = "name",
    Phone = "phone"
};

var mockRepository = new Mock<IRepository>();
var mockUrlHelper = new Mock<IUrlHelper>();
mockUrlHelper.Setup(x => x.GetLink(It.IsAny<string>(), It.IsAny<object>()))
    .Returns("http://localhost/webapi/employee/");
mockRepository.Setup(x => x.Create(It.IsAny<Employee>())).
    Callback(() => isCreatedInvokedInRepository = true);
var controller = new EmployeeController(
    mockRepository.Object, mockUrlHelper.Object);
controller.Request = new HttpRequestMessage()
{
    Properties = { { HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration() } }
};

//ACT
var response = controller.Post(employee);

//ASSERT
Assert.IsTrue(isCreatedInvokedInRepository,
    "Create method in Repository not invoked");
Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
Assert.IsNotNull(response.Headers.Location);

You can write similar tests for all the other methods.

Happy Unit testing.



Did you like this article? Share it with your friends

Facebook Twitter Google+ LinkedIn Reddit StumbleUpon



MVP
Subscribe
Archives


Copyright © 2017 - Anuraj P. Blog content licensed under the Creative Commons CC BY 2.5 | Unless otherwise stated or granted, code samples licensed under the MIT license. This is a personal blog. The opinions expressed here represent my own and not those of my employer. Hosted with ❤ by GitHub