Ryan LanciauxNew Media Mercenary

Gherkin style specification testing in .NET

January 31, 2010 by ryan
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
          



Beginning ASP.NET MVC 1.0 available on Amazon

August 7, 2009 by ryan

I know I've been quiet for a little bit here but wanted to point out some great news. Simone Chiaretta and Keyvan Nayyeri's book Beginning ASP.NET MVC 1.0 is available for purchase in the US on Amazon. Both Simone and Keyvan have a wealth of knowledge about the framework, so I'm really looking forward to the book. For more information check out

 




Are you a Control Freak?

March 24, 2009 by ryan

No Controls?
Since the very early ASP.NET MVC previews, the most common argument I've heard against using the framework is the lack of user controls. In my opinion, this is a mixed blessing, however, for some, this is a show stopper. What can you do to provide a rich user interface with the MVC framework, while not reinventing the wheel?

jQuery
Now you could go ahead and program a full fledged data grid or WYSIWYG editor, however, unless you have a bit of time to spare this is not the ideal solution. As most may realize, jQuery fits very nicely to fill in the gaps left by the absence of user controls. For the most part, the controls are not going to let you just drag-and-drop, type in a dataset and profit but a lot of the pain is kept to a minimum.

Examples
Ingrid

If you're looking for a data grid there are a number of options available pictured above is Ingrid. Flexigrid is another good one that people have used with the MVC framework, however, their site appears to be down at the moment.


Treeviews are available

Radio Buttons / Check Boxes

Check out the jQuery plugin database.

Getting Started
So, if you've decided you want to look more into using jQuery plugins -- a couple of tips that may make the transition a little easier:

  • Read up on what people using different languages are doing. I'm not saying that you should necessarily go out and learn Rails or CakePHP/CodeIgniter (although, it is beneficial). but at least look at the techniques that programmers from that realm of things are doing -- see how they are implementing their controls.

What tips do you have for creating robust user interfaces with the MVC framework?
kick it on DotNetKicks.com




Findlay .NET UserGroup Recap

January 27, 2009 by ryan

I had a great time tonight at the Findlay .NET User group! Thanks everyone that battled the weather to come out and hear me talk about the ASP.NET MVC Framework. As promised, I have posted the route constraint to allow only alphabetical values to be accepted as valid input for the display named message.

  routes.MapRoute("Message",

                 "Message/{action}/name/{Name}/",

                 new { controller = "Message", action = "HelloWorld", name = "" },

                 new {name="[a-zA-Z]+"}

  );


I apologize about the lapse in memory -- The regular expression needed a + at the end to signify that it was more than one character whoops :)  For more information on Routes check out Justin Etheredge's post on routing


MVC Framework Talk : Findlay .NET User Group

January 23, 2009 by ryan
I will be giving a talk on the ASP.NET MVC Framework at the Findlay .NET User Group on Tuesday, January 27th (5:30pm - 7:30pm). If you're in NW Ohio and want to learn more about the MVC framework and how to get up and running with it you might be interested. The talk is taking place at the Marathon building downtown -- 539 S. Main St., Findlay, OH - Room 106M. More information is available at the User Group site. Hope to see you there!


Do you suffer from this condition?

December 17, 2008 by ryan

Oxitis [oks - ahy - tis]
-Noun

Acute or chronic anxiety toward sharing code online; this anxiety is usually caused by fear of public ridicule or excessive criticism regarding imperfections in code.

Origin: 
2008, Josh Schwartzberg (n.); Oxitephobia; avoidance of placing imperfect code online; initially referring to Microsoft Oxite


kick it on DotNetKicks.com




MVC HtmlHelper for Gravatar

November 13, 2008 by ryan

Based on a great suggestion from Ben Scheirman and Simone Chiaretta I have converted the Gravatar URL helper class into a HtmlHelper extension method. This was my first stab at making a HtmlHelper extension but it seems to be working nicely and helps me keep my code a lot cleaner. Instead saving the URL (which I would highly advise against) or doing some weird method calling acrobatics in the Controller, I can simply say <%= Html.Gravatar("email@email.com") %> inside the view.

I've added a couple different method definitions for various parameters. You can pass in just an e-mail address, an e-mail and gravatar parameters (see here for more info on gravatar parameters) or an e-mail address, gravatar parameters and html attributes. A quick usage summary is listed below (I'm using my brother Joel's gravatar in the examples)


<%= Html.Gravatar("email@email.com") %>

Base Gravatar Helper 

 

<%= Html.Gravatar("email@email.com", new  { s = "128", r = "pg" })%>

Gravatar helper with optional Gravatar Parameters

 

<%= Html.Gravatar("email@email.com", null, new  { style = "border:5px solid" })%>  

Helper with HTML attributes. Please note, this is still expecting the Gravatar parameters -- you can pass in null if you want to just grab the default image with no additional properties.

 

<%= Html.Gravatar("email@email.com", new { s = "128", r = "pg" }, new { style="border:5px solid;"})%>

Helper with both Gravatar parameters and HTML attributes

 

Anyways, I've posted the class file below. Hopefully you find it useful -- I would love to hear what your thoughts are or of any changes you would like to see.
GravatarHelper.cs (2.72 kb)




Gravatar on ASP.NET MVC

November 9, 2008 by ryan

UPDATE: See here

In the near future, I have a web project coming out that I've been working on for the past month or so. I'm not going to give too much detail on that just yet, however, in working on this project, I had to come up with a way for users to have profile images. I would rather not host these images on my server, I decided to leverage Gravatar (Globally Recognized Avatars).

There are a couple pre-existing options for dealing with gravatar in the .NET framework, however, I'm using the MVC framework -- standard ASP.NET controls are out of the question. After a brief look on the Gravatar site (specifically the page dealing with how the URL is constructed), it seemed like this would be a pretty easy task to write a class that I could use to get gravatar image paths. Below is the main part of the class I created to retrieve gravatar information based on an e-mail address:


        public static string GetGravatarURL(string email, string size)

        {

            return (string.Format("http://www.gravatar.com/avatar/{0}?s={1}&r=PG",

                EncryptMD5(email), size));

        }

 

        public static string GetGravatarURL(string email, string size, string defaultImagePath)

        {

            return GetGravatarURL(email, size) +  string.Format("&default={0}", defaultImagePath);

        }

 

        private static string EncryptMD5(string Value)

        {

            System.Security.Cryptography.MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();

            byte[] valueArray = System.Text.Encoding.ASCII.GetBytes(Value);

            valueArray = md5.ComputeHash(valueArray);

            string encrypted = "";

            for (int i = 0; i < valueArray.Length; i++)

                encrypted += valueArray[i].ToString("x2").ToLower();

            return encrypted;

        }


All we're really doing is creating a URL based on the Gravatar path, an MD5 encrypted version of an e-mail address and some user specified parameters. For the methods, Size is how many pixels the image should be (up to 500) and defaultImagePath is the url to use as an alternate image if the user does not have a Gravatar image. There is a rating parameter also but for my site, I'm setting it to PG always.

This is not all that difficult but hopefully useful. I plan to continue writing examples as I make progress on the web application I mentioned.





What is your strategy for becoming a better developer?

September 21, 2008 by ryan

Justin Etheredge recently posted a question for the community on his site. I think this is a good thing to think about because my definition of a good developer is one who continually tries to be better at their craft. My suggestions may be very similar to others but I would love to hear feedback.

Read : the first suggestion I have is a bit obvious. I like to read books and blogs on programming -- especially methodologies and architecture. Its hard to find good books becuase there seems to be an over saturation, many of which are not good. That being said, there are quite a few that I would still recommend : Head First Design Patterns, Don't Make Me Think (not really a programming book but important for anyone that writes applications with end-users) and Code Complete. Additionally I'm really looking foward to Beginning ASP.NET MVC by Simone and Keyvan. As for blogs, there are tons I subscribe to but a few I'll mention are Justin's, Jurgen Appelo's and Dustin Campbell's.

Share :  I first started my site to help myself and hopefully others with programming topics. As I chose a subject to write about, I realized I did a lot more research than I would if it was just something I wanted to learn. Additionally, there was a lot of experience that I gained from the comments on the articles. Sharing information with a high level of transparency helps you become a better developer because you will get feedback on your work.

Set Goals : Set goals to learn new languages / techniques. I start off by reading blogs/books/articles then think of achievable pet project to use these new techniques on. The progress on the project can be used as the baseline for determining your progress. This is not saying you would be an expert in the new area but, in my opinion, is one of the best ways to learn.

There are tons of additional ways to become a better developer and I would love to hear your suggestions!

 




MVC Framework Talk

August 19, 2008 by ryan
Tonight I have the privilege to be giving a talk on the ASP.NET MVC Framework at the Northwest Ohio .NET User Group! (6PM, at the HCR Manor Care building downtown)! If you interested in learning about some of the reasons for using the MVC framework along with creating a basic MVC application you might want to check it out :)






© 2008 Ryan Lanciaux :: powered by BlogEngine.NET