I wanted to start on my project, and I thought the first thing to start on was to display a list of policies. As I want to have a Single Page Application, I have started out creating an API.
Roy Fielding is the author of Representational State Transfer or REST. There are lots of comments about REST on the web, but I prefer the Richardson model for defining REST
- Level 0 – POX
- Level 1 – Resources
- Level 2 – HTTP Verbs
- Level 3 – Hypermedia or HATEOAS.
So the API that will be built will be at least level 2, but the aim is for Level 3. I like using the http verbs to retrieve resources. However I know that it is extremely difficult to not stray into RPC calls.
The default MVC project template includes routes for controllers and for api. For now will use the default convention.
Start off by creating a new folder called api. When creating a api controller, it inherits from ApiController. I know that I am not going to need it just now, but I always prefer having a base class instead, even if that class is empty, and then update my controllers to inherit from this base class.
public class BaseApiController : ApiController { }
While not needed yet, the base class will be used for logging and exception handling at a future date.
Lets start on the api by creating an empty class PolicyController.
public class PolicyController : BaseApiController { public PolicyController() { } }
Now that we have our base class, its time to create the test class within the Test project. I prefer to have matching folders for projects that I am testing. This is personal preference only. Within the MotorDB.Tests project, create a folder ApiControllerTests, and then create the test class PolicyControllerTests
The first test I create is to ensure that I can create the controller:
private PolicyController GetControllerForTests() { return new PolicyController(); } [Test] public void Can_Instantiate_Web_Api_Controller() { Assert.DoesNotThrow(() => GetControllerForTests()); }
With this method it also helps me with later tests so that I have one place that creates a controller. It makes it easier when later coding when I need to inject various classes, I only need to alter the code in one location.
Now with that out of the way, there is more setup that is required for the controller. Next step is to create an extension that will be used by all controllers for testing purposed that deals with routing.
public static class Extensions { public static T SetupControllerConfigs(this T controller) where T : ApiController { var config = new HttpConfiguration(); var request = new HttpRequestMessage(HttpMethod.Post, "http://server.com/foo"); var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}"); var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", controller.GetType().Name } }); controller.ControllerContext = new HttpControllerContext(config, routeData, request); controller.Request = request; controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config; return controller; } }
There are different ways of mocking this out, but this is the method that I prefer. Finally update the GetControllerForTests() method
private PolicyController GetControllerForTests() { return new PolicyController().SetupControllerConfigs(); }
Make sure everything passes, and now its time for the next test. Calling the Get method on the controller
[Test] public void When_Call_Get_Method_Then_Expected_Status_Code_Ok() { var controller = GetControllerForTests(); var response = controller.Get(); Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); }
Everything passes and now to move on to actually retrieving some data.
[Test] public void When_Call_Get_Then_Expected_Data_Is_Returned() { var controller = GetControllerForTests(); var response = controller.Get(); Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); var returnedObjects = response.Content.ReadAsStringAsync().Result; var returnedListOfPolicy = (List)JsonConvert.DeserializeObject(returnedObjects, typeof(List)); Assert.That(returnedListOfPolicy.Count, 2); }
The test wont even compile until we have created a few classes.
First thing is to create a class for a policy.
public class Policy { public int Identifier { get; set; } public string PolicyNumber { get; set; } public string PolicyholderName { get; set; } public IList PolicyPeriods { get; set; } } public class PolicyPeriod { public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } }
Test is still failing, we need an interface to get the data. So create an interface based on the repository pattern. At present we are only interested in getting data.
public interface IPolicyRepository { IList Get(); Policy GetPolicyFor(int identifier); }
Now the controller will use the interface to retrieve and return the data.
public class PolicyController : BaseApiController { private readonly IPolicyRepository _policyRepository; public PolicyController() { } public HttpResponseMessage Get() { var policyDataToReturn = _policyRepository.Value.Get(); var response = Request.CreateResponse(HttpStatusCode.OK, policyDataToReturn); return response; } }
Now we need to inject a fake into the controller so that we can complete the test
At this point should use an IoC container, but I don’t want to go onto this level of detail just now, so will use little trick, Create an in memory database that inherits from the interface, and use the Lazy keyword.
public class PolicyRepository : IPolicyRepository { private static List _database; public PolicyRepository() { _database = new List(); var policy = new Policy { Identifier = 1, PolicyholderName = "Policyholder 1", PolicyNumber = "1001", PolicyPeriods = new List() { new PolicyPeriod { StartDate = DateTime.Parse("2011-02-01"), EndDate = DateTime.Parse("2012-01-31") }, new PolicyPeriod { StartDate = DateTime.Parse("2012-02-01"), EndDate = DateTime.Parse("2013-01-31") }, new PolicyPeriod { StartDate = DateTime.Parse("2013-02-01"), EndDate = DateTime.Parse("2014-01-31") } } }; _database.Add(policy); policy = new Policy { Identifier = 2, PolicyholderName = "Policyholder 2", PolicyNumber = "1002", PolicyPeriods = new List { new PolicyPeriod { StartDate = DateTime.Parse("2012-05-01"), EndDate = DateTime.Parse("2013-04-30") }, new PolicyPeriod { StartDate = DateTime.Parse("2013-05-01"), EndDate = DateTime.Parse("2014-04-30") } } }; _database.Add(policy); policy = new Policy { Identifier = 1, PolicyholderName = "Policyholder 3", PolicyNumber = "1003", PolicyPeriods = new List { new PolicyPeriod { StartDate = DateTime.Parse("2013-06-01"), EndDate = DateTime.Parse("2014-05-31") } } }; _database.Add(policy); } public IList Get() { return _database; } public Policy GetPolicyFor(int identifier) { return _database.FirstOrDefault(f => f.Identifier == identifier); } }
Eventually this will be updated with a call to a real repository, but its the minimum that is needed for now. Finally update the contoller to
public class PolicyController : BaseApiController { private readonly Lazy _policyRepository = new Lazy(() => new PolicyRepository()); public PolicyController() { } public PolicyController(IPolicyRepository policyRepository) { _policyRepository = new Lazy(() => policyRepository); } public HttpResponseMessage Get() { var policyDataToReturn = _policyRepository.Value.Get(); var response = Request.CreateResponse(HttpStatusCode.OK, policyDataToReturn); return response; } }
The Lazy keyword is a nice trick for helping to implement Poor Man’s Dependency Injection.
Now for the test, we just need to pass in a mock repository and test again. All that is needed is to update GetControllerForTests method.
private PolicyController GetControllerForTests() { return new PolicyController(_mockPolicyRepository.Object).SetupControllerConfigs(); }
Run the tests and everything’s green
There are more tests that can be created. e.g exception handling, and calling Get with an identifier, but this is enough for now.
I have not pushed the code up to github or appharbor yet. That will only do when have updated the UI project to display the policy information. That will be the topic of the next post
[…] a previous post, I started a restful API that returns policy information. Now its time to starting calling the API […]
[…] In previous post I created a RESTful web service to return policy data to the client. […]