Gherkin style BDD testing in .NET

Behavior Driven Development is something that has interested me forquite awhile. I have constantly tried to write my tests as clear andconcise as possible but once I saw Cucumber for Ruby that became thenew standard for what I was trying to achieve in .NET. This is whereSpecFlow comes in.

 

SpecFlow is a BDD library for .NET that aimsto add testing capabilities that are similar to Cucumber — that is,specifications are written in human readable Gherkin Format. From theproject site

SpecFlow aims at bridging the communication gap between domain experts and developers by binding business readable behavior specifications to the underlying implementation.

In theory, I really like that domain experts could write the specifications but I would be interested in seeing how that works out.

So what exactly is this Gherkin format?

According to the Gherkin project on github, 'Gherkin is the language that Cucumber understands. It is a Business Readable, Domain Specific Language that lets you describe software’s behaviour without detailing how that behaviour is implemented.' In other words, its a common DSL for describing the required functionality for a given system. 

This functionality is typically broken down by feature and each feature has a number of scenarios. A scenario is made up of 3 steps: GIVEN, WHEN and THEN (which seems to somewhat loosely correspond to Arrange, Act, Assert) and in a simplistic world, looks a little like this:

GIVEN an admin user
WHEN user requests top secret data
THEN return the list of data

If you want to learn more about the Gherkin format check out Engine Yard's Introduction to BDD with Cucumber by Dave Astels or Given-When-Then by Aslak Hellesøy

Quick Synopsis

I've recently started to move my blog over to a new server and a new root domain name; this could have an adverse affect on inbound links. In order to make sure this move was successful, I wanted to write an app to perform 301 redirects from the old URL to the new one.

There are a number of examples out there already for performing 301s but I wanted to make sure I was testing the code — It seemed like a great opportunity to get a little more use out of SpecFlow.

Initial Setup

  1. Download and run the SpecFlow installer
  2. Create a new Project and add a reference to SpecFlow and NUnit Framework
  3. Add references to your mocking framework (this example is using Moq)

On with the code!

After all the references are sorted out add a SpecFlow feature.

The feature file is where we're going to define our specifications. I want to make sure that when a request is made to the old root it will get redirect to the new root url. So here is what the feature looks like initially:

Feature: Redirection
    In order to not upset the google
    As a blogger who almost never has the time to blog
    I want to redirect my old url to my new one

Scenario: Redirect root request
    Given I have entered a request to http://www.frickinsweet.com/ryanlanciaux.com
    And the old url is frickinsweet.com/ryanlanciaux.com
    And my new url is ryanlanciaux.com
    When the request is made
    Then the response url is http://www.ryanlanciaux.com
    And the response has a 301 in the status

Notice that over in the Solution Explorer window you can expand the feature to reveal a .cs file.

The class is an auto-generated file that updates when the .feature file is changed. We can run this through our test runner to watch it fail and get some extra information as to why it failed.

 

The tests are failing because there is no real definition to the scenario steps. We can almost directly copy and paste the output from the test runner dialog to a new class and fill in the code for the methods with standard unit testing code. Like I said before, I treat everything that is a GIVEN statement like an arrange section of a standard test; WHEN and THEN like act and assert respectively.

We could hardcode these tests to be specifically run against the urls specified in the scenario but this approach feels brittle and does not encourage code reuse. In order to use these these same steps in our future scenarios we can add wildcard mappings rather than specifying a single url in the attribute definition. The wildcard mapping is the familiar .* surrounded by parenthesis.

   24         [Given(@"I have entered a request to (.*)")]
 

Also note now that when we have a wildcard mapping, we can pass in a parameter to that ScenarioStepDefinition

public void GivenIHaveEnteredARequestToHttpWww_Frickinsweet_ComRyanlanciaux_ComPage2(string url)

The final result is a lot of code but it is broken down into small, reusable sections.

[TestFixture]
[Binding]
public class RedirectTest
{
    private string oldUrl;
    private string newUrl;
    private string requestedUrl;
    private string finalUrl;
 
    private RedirectHandler _handler;
    private Mock<HttpContextBase> mockContext;
    private Mock<HttpResponseBase> mockResponse;
 
 
    [Given(@"I have entered a request to (.*)")]
    public void GivenIHaveEnteredARequestToHttpWww_Frickinsweet_ComRyanlanciaux_ComPage2(string url)
    {
        var uri = new Uri(url);
        requestedUrl = url;
 
        mockContext = new Mock<HttpContextBase>();
        mockContext.Setup(x => x.Request.Url).Returns(uri);
 
    }
 
    [Given(@"the old url is (.*)")]
    public void GivenTheOldUrlIsFrickinsweet_ComRyanlanciaux_Com(string url)
    {
        oldUrl = url;
    }
 
    [Given(@"my new url is (.*)")]
    public void GivenMyNewUrlIsRyanlanciaux_Com(string url)
    {
        newUrl = url;
 
        //now that we know both old and new url do a replace on httpcontexts' url
        //setup what we expect the called url to be and throw a callback on the mock so we can verify later
        mockResponse = new Mock<HttpResponseBase>();
        mockResponse.SetupProperty(x => x.Status);
        mockResponse.Setup(x => x.AddHeader("Location", requestedUrl.Replace(oldUrl, newUrl)))
            .Callback(() => finalUrl = requestedUrl.Replace(oldUrl, newUrl));
 
        mockContext.Setup(x => x.Response).Returns(mockResponse.Object);
 
    }
 
    [When(@"the request is made")]
    public void WhenTheRequestIsMade()
    {
        _handler = new RedirectHandler();
        _handler.ProcessRequest(mockContext.Object, oldUrl, newUrl);
    }
 
    [Then(@"the response has a 301 in the status")]
    public void ThenTheResponseHasA301InTheStatus()
    {
        Assert.That(mockContext.Object.Response.Status == "301 Moved Permanently");
    }
 
    [Then(@"the response url is (.*)")]
    public void ThenTheResponseUrlIsTheNewUrl(string expectedUrl)
    {
        Assert.AreEqual(expectedUrl, finalUrl);
    }
 
    [Then(@"301 is not in the headers")]
    public void Then_301IsNotInTheHeaders()
    {
        Assert.IsNull(mockResponse.Object.Status);
    }
}

Since we are using wildcards instead of raw urls in the step definitions we can easily write other tests that will just work with out adding any extra code.

Scenario: Redirect to correct path on new url
    Given I have entered a request to http://www.frickinsweet.com/ryanlanciaux.com/page2
    And the old url is frickinsweet.com/ryanlanciaux.com
    And my new url is ryanlanciaux.com
    When the request is made
    Then the response url is http://www.ryanlanciaux.com/page2
    And the response has a 301 in the status

This project, in its entirety, is hosted on GitHub. Check it out if you are interested in seeing SpecFlow in the context of the whole (tiny) application. Make sure that you add all the files from the lib dir into your references the first time you run it or you will receive all kinds of errors — additionally, I wrote this quickly for myself so there is no real warranty / guarantee that the code is free from defects — use at your own risk. :)

Download Project from GitHub

Visit the SpecFlow homepage 

 

Pass it along


          

IE8 vs Firefox 3.0b5 : JavaScript Part 1

Last time I posted on the latest browser from Microsoft vs. Mozilla's latest I had compared page download times between the two browsers. This time, I would like to take a brief look at how they stack up in regard to JavaScript.

John Resig (of jQuery / Mozilla fame) announced the Dromaeo: Javascript Performance Testing suite yesterday. The suite runs some of the same tests as the WebKit team's SunSpider, however, there are some improved methodolgies (see the suite's wiki for more info on this). I don't know how often I feel like doing DNA Sequence Alignment in JavaScript but I think it would be a good way to quickly get some metrics of how both FireFox and IE8 handle the code.

The Comparison:

I ran the tests in groups because there were certain ones that were not finishing under IE8. To be quite honest, I'm not 100% sure if this is an Internet Explorer issue, or an issue with the tests. Both are a work in progress so I would rather not make assumptions at this point. Anyways, the results of the tests are listed below:

Test Name Firefox 3.0 beta 5 Internet Explorer 8
Fannkuch v122 802.80ms 1032.20ms
Base 64 Encoding and Decoding v122: 2942.00ms Would not finish
DNA Sequence Alignment v116: 420.00ms Would not finish
N-Body Rotation and Gravity v122: 270ms 529.40ms
Prime Number Computation (2) v122: 305.80ms 518.20ms
Recursive Number Calculation v122: 184.40ms 417.80ms
Traversing Binary Trees v122: 156.80ms 422.20ms

The Results

The initial results look like Firefox is performing the more advanced Javascript operations faster than Internet Explorer. That being said, Dromaeo, IE8 and Firefox 3.0b5 are still being developed so a lot could change before the release. For more metrics view a comparison on the Dromaeo site of Safari 3.1, Firefox 3.0b5, Opera 9.5 and IE8b1. Let me know what you think or if you've encountered different results.


kick it on DotNetKicks.com