Monday, 6 August 2012

Specflow BDD test with HttpClient

I've been using Specflow for a while now for various BDD testing scenarios. However until now I'd not come across an example that I'd like to blog about.Enter: HttpClient and HttpRequestMessage included in .net 4.5 under the System.Net.Http namespace.

Scenario

The story starts with the requirement to drop a client cookie so we know which users have visited the site before. And perform some basic logging about the visit details, query string information, referrer etc.Straight forward enough, but the complexity arises because we want a single Visit Tracking Utility that spans multiple sites, each on a different platform and written in different languages. Some are even 3rd party sites, so we have minimal control over the html displayed - and zero control over whats going on at the server. In order to achieve this platform-agnostic solution we need to solve the problem with javascript as we accept will have at least this much control over each target site. We also have some specification about the name and value of the cookie as well as the expiry date date.The technical requirement therefore is a url available to make requests to that will drop a cookie on the clients machine called VisitId, with a Guid value that expires in 7 days time.

The BDD Test

If you are not already, please familiarise with specflow. The plan here is to capture the Behaviour in a Feature file that we can make assertions from based on our code. We begin by citing the scenario we want to test using the Gherkin Given; When; Then format. Specflow then allows us to attach code to the steps we dictate, then we insert the required code and necessary NUnit assertions to validate our scenario.Starting with a new IntegrationTest project in the solution we add a new VisitTracking feature file and enter the following scernario.

Feature: Visit Tracking
In order to know which users have previously visited
I want to drop a client cookie on each visit
Scenario: Setting client cookie for a new visit
Given the api uri is local.trackmyvisit.com/api/trackvisit
And the expected cookie name is VisitId
When I hit the visit tracking uri
Then the response HttpCode is OK
And the response sets a cookie
And the cookie name is correct
And the cookie value is a valid Guid
And the cookie expiry is 7 days from now

Each 'Then' and following 'And' gives a chance to make an assertion about the request we have made. We are testing exactly the things described in our specification.If we build and try to run this now, then specflow will tell us that we are missing the steps specified in the scenario:

-> No matching step definition found for the step. Use the following code to create one:
[When(@"I hit the visit tracking uri")]
public void WhenIHitTheVisitTrackingUri()
{
ScenarioContext.Current.Pending();
}

Copy from the test output all the necessary steps into a VisitTrackingSteps class. With a couple of extra helper methods and paramter arguments on your class should look like the following. Don't forget to use the [Binding] attribute before the class declaration or specflow won't know where to look for the steps. Also you will need to install the System.Net.Http package to get reference to the HttpClient object et al.

You can see that in the 'When' method the HttpClient and HttpRequestMessage are used to initiate the call to the visit tracking site - at present not yet created. The SendAsync method of the HttpClient is called using a GET request to the path of the api controller method. This is replicating exactly what the script tag will do from each web page it appears on. So we have been able to quickly conjure up a http request without the use of selenium or other web driver. The other nice part is that we can interrogate the response from the request by accessing the Result property of the generic Task we get from calling SendASync.The tests will build and run now but there is currently nothing to hit so the tests will fail. We can now implement the logic that will fulfil the spec.

Tracking Logic using an Api Controller

Our spec tells us we need a uri we can hit that will perform the visit tracking logic and write back a cookie to the client through the http response. Furthermore, as there will be now javascript or html returned in the response (i.e. nothing for the client to parse) this sounds like a candidate for using an MVC4 WebApi Controller. The controller implements the exact requirements of the spec.