Anatomy of a very basic SpecFlow test
on January 7th, 2012 at 1:13 pmTo start off, I’d like to apologize about not posting this earlier. Both life and work got very busy. A tweet from a reader reminded me about my claim that I’d post some more in the future, and it looks as though I have a little bit of spare time this weekend. I’ll start posting some of the things that I’ve learned about SpecFlow, any gotcha’s that I may not have figured out yet and some other personal experiences. For more detailed information, make sure to check out SpecFlow’s page.
Anyways, let’s begin. I’m going to assume that you’ve already got SpecFlow installed in Visual Studio, so I won’t cover that part. We’ll start out with a blank solution and a class library project.
There was the default class1.cs in the project, but, we can delete that as we’re not going to need it for this. I would typically keep it however if I need to experiment with code that I’m not familiar with and don’t want to butcher the tests that I’ll already have written.
Next, we need to add a feature file to our solution. In this case, I’m going to test the search functionality that I just added to this blog, so, for simplicity sake, I’m just going to name the file “search.feature”.
The file is going to include a template story and scenario. If you’re just starting to get used to agile development or gherkin, this can be useful to help you format your tests.
Instead, we’re going to replace it with our own story (feature) and acceptance criteria (scenario, or, test).
Let’s start by breaking this down a little bit. We’ll start with the Feature part of the feature file.
The feature is the story. It’s a high level overview of what a user should be able to do. If you need more information of how a story should be formatted, I suggest you search for one of the many agile sites or blogs, especially, something on gherkin.
The next part of this is the Scenario. This would essentially be considered your acceptance criteria. In simpler terms, for those not familiar with the term, it’s a test.
Scenario: Search for text
Given I have entered the text "testing" into the search text field
When I press the search button
Then the I am taken to a search results page
And I am told what I searched for
And how many results are being displayed
And I am given a list of posts that meet my criteria
The first part of the scenario is the name. A high level description of what you are testing.
Next, we have the Given parameter. Anything in the Given section of your scenario should be like a setup for the individual test. In this case, I’ve said that I’m going to enter the string “testing” into the search text field. If we need the same setup done for each method, we can use the [BeforeScenario] tag, but, we’ll cover that in a later post. If we need multiple lines, we can add an “And” on the next line. Each And will count as the previous parameter until we get to another one. We’ll look more into this when we get to “Then”
The When parameter is what we are going to do. This would be akin to the step description in a classical test case or the actual action that we are performing. In this scenario, I’ve said that I am going to press the Search button.
Finally, I enter my Then. This is the verification and/or validation part of the scenario. We are making certain that something happened. In this scenario, I am checking for 4 separate things:
1) My browser has navigated to the search results page.
2) The text that I searched for is on the page.
3) The number of results is displayed.
4) Previous blog posts that match the search criteria are displayed on the page.
There, we now have our feature (story) and Scenario (Test). Let’s write some code.
In SpecFlow, all test code is pulled from a StepDefinition file. Let’s add one. We’ll add a new file to our project, in this case, a SpecFlow Step Definition. The default will be StepDefinition1.cs. For cleanliness sake, I normally rename this to StepDefinition.cs.
Again, this will begin with some default template code. Let’s delete this, leaving just the class. We could begin writing code now, but, I tend to be kind of lazy, so, I’m going to get SpecFlow and NUnit write some of it for me. At this point, we’re going to build the solution.
We can see that our scenarios have been parsed and assembled into unique tests.
Let’s run this. It’s going to pass, as we don’t have any code to run yet, but, more importantly, because we don’t have any of the code written yet, it’s going to tell use what we need.
We can now take the methods and the tags associated with each of them, and paste them into our StepDefinition.cs file. Please note, that if you have multiple tests that have the same step in them, you will find duplicates.
Now that we have our code, let’s try to run these tests again. Again, the tests will pass again, this time though, we’re want to be told on each step that the step is pending. If you see any tests where it gives you code for, you know that you’ve missed something.
I suppose that I should take the time to explain now how SpecFlow finds each of these step definitions for each step. Essentially, it takes each line from your Feature file, and attempts a match against the tags in your step definition file.
Now, let’s really start writing some code. Each step should do 1 thing, and one thing only. You’ll notice though that in my Given’s, I forgot about adding a step to the beginning, and put the code to navigate to this site in there instead.
1: using System;
2: using TechTalk.SpecFlow;
3: using NUnit.Framework;
4: using OpenQA.Selenium;
5: using OpenQA.Selenium.IE;
6: using System.Text.RegularExpressions;
7:
8: namespace musingtesterDotCom
9: {
10: [Binding]
11: public class StepDefinition
12: {
13:
14: public IWebDriver _browser = null;
15: public IWebDriver browser
16: {
17: get
18: {
19: if (_browser == null)
20: _browser = new InternetExplorerDriver();
21:
22: return _browser;
23: }
24: }
25: string siteUrl = "http://musingtester.com";
26:
27: [Given(@"I have left the search text field blank")]
28: public void GivenIHaveLeftTheSearchTextFieldBlank()
29: {
30: browser.Navigate().GoToUrl(siteUrl);
31: }
32:
33: [When(@"I press the search button")]
34: public void WhenIPressTheSearchButton()
35: {
36: browser.FindElement(By.XPath("/html/body/div[2]/div/div[4]/div[2]/div/div[2]/div[2]/div[2]/form/button")).Click();
37: }
38:
39: [Then(@"the I am taken to a search results page")]
40: public void ThenTheIAmTakenToASearchResultsPage()
41: {
42: Assert.IsTrue(browser.Url.Contains("http://musingtester.com/?s=testing"));
43: }
44:
45: [Then(@"I am told what I searched for")]
46: public void ThenIAmToldWhatISearchedFor()
47: {
48: Match SearchString = Regex.Match(browser.FindElement(By.XPath("/html/body/div[2]/div/div[4]/div[2]/div[3]/h2")).Text, @"Search for (.*?)");
49: Assert.IsTrue(SearchString.Success);
50: }
51:
52: [Then(@"how many results are being displayed")]
53: public void ThenHowManyResultsAreBeingDisplayed()
54: {
55: string resultsString = browser.FindElement(By.XPath("/html/body/div[2]/div/div[4]/div[2]/div[3]/div")).Text;
56: Match stringNumber = Regex.Match(resultsString, "(.*?) Results", RegexOptions.IgnoreCase);
57: double numberOfResults = 0;
58: if (stringNumber.Success)
59: {
60: numberOfResults = Convert.ToDouble(stringNumber.Groups[1].Value);
61: }
62: Assert.IsTrue(numberOfResults > 0);
63: }
64:
65: [Then(@"I am given a list of posts that meet my criteria")]
66: public void ThenIAmGivenAListOfPostsThatMeetMyCriteria()
67: {
68: browser.FindElement(By.PartialLinkText("Cache"));
69: }
70:
71: [Given(@"I have entered the text testing into the search text field")]
72: public void GivenIHaveEnteredTheTextTestingIntoTheSearchTextField()
73: {
74: browser.Navigate().GoToUrl(siteUrl);
75: browser.FindElement(By.Name("s")).SendKeys("testing");
76: }
77:
78: }
79: }
Now, we can run our tests again. We’ll see that Internet Explorer opens, navigates to the site, enters either specific text or nothing, clicks on the search button, and then checks what we wanted it to.
Next up, Step scope and SpecFlow’s version of setUp and tearDown. Stay tuned

Nice intro, waiting to read next part!