Just Behave! Part 2 of 2

In the previous post, we built the steps necessary to convert our coded HelloWorld test into a NBehave test.

In this post, we will build the NBehave test runner and use the steps we built in the previous post to run our HelloWorld test. To accomplish this we need 2 things.

  • Extensions class that contains methods for kicking off and validating results of the tests.
  • Feature files.

Extensions Class

Create a new class in the HelloWorld project named Extensions.cs.

The first method we will add is the ExecuteTest method.

public static FeatureResults ExecuteTest(this string path)
{
    return NBehaveConfiguration
        .New
        .DontIsolateInAppDomain()
        .SetEventListener(new OutputEventListener(new ConsoleWriter()))
        .SetAssemblies(new[] { typeof(HelloWorld.Google).Assembly.Location })
        .SetScenarioFiles(new[] { path })
        .Build()
        .Run();
}

This code sets up and kicks off a NBehave test and returns the results.

Next we need a method to validate the results.

public static void AssertTestResults(this FeatureResults results)
{
    if (results.NumberOfScenariosFound == 0)
        Assert.Inconslusive("No scenarios have been run");
    else
        Assert.AreEqual(results.NumberOfScenariosFound, results.NumberOfPassingScenarios);
}

This code returns an inconclusive result if no scenarios have been run. If scenarios have been run, we compare the number of scenarios ran to the number of scenarios that passed to determine if our tests passed or failed.

Next we need to build our .feature files. Feature files are the files that contain the NBehave tests. They are just regular old text files with a .feature extension.

Using Windows explorer, create a file named Google.feature in the same folder as the HelloWorld project. Then in the VS solution explorer right click on the HelloWorld project and select Add -> Existing Item… and add the new file to your project.

Add the following text to Google.feature:

Feature: Google
Validate Google functionality
	Scenario: Search for "Hello World!"
		Given a Firefox browser
		When I visit http://www.google.com/
		And I search for "Hello World!"
		Then the help button exists

This text defines a feature, “Google”, whose purpose is to “Validate Google functionality”. The scenario were testing is “Search for ‘Hello World!'” and the rest of the file contains references to the steps we built in part 1 of this tutorial.

The last thing to do is to change the SearchForHelloWorld method in the Google class from a coded test to an NBehave test runner. Replace the SearchForHelloWorld method with this method.

[TestMethod]
public void SearchForHelloWorld()
{
    string test = Environment.CurrentDirectory + @"\Google.feature";
    test.ExecuteTest().AssertTestResults();
}

When you kick off the SearchForHelloWorld test, its now driven by the feature file and executed with the NBehave steps!

Just Behave! Part 1 of 2

The advantages of using Gherkin with Cucumber are well documented. This is the first of two posts that describe how to integrate NBehave (the .NET port of Cucumber) into your test project, specifically our Hello World! test project.

There are frameworks that support Gherkin for many languages, such as JBehave for java, Behave.js for javascript, or Behave for python. This post will describe how to implement NBehave, which is a C# solution.

Get the NuGet Package

The first step in implementing NBehave is to get its NuGet package. The name of the package is NBehave and it can be added to your project using the process described in the Hello World! post.

Create the NBehave steps

The biggest advantage NBehave offers is the ability to write your tests in a language that non-technical users may be able to understand.

The individual units that make up NBehave tests are called steps. To create these steps, we will add a class named Steps to our Hello World! project. Steps allow sections of code, written in C#, to be executed via strings, thus allowing tests to be written by non-technical stakeholders, such as product owners or QA engineers w/o programming experience.

We will need the NBehave library as well as the Selenium libraries to build the steps.

using NBehave.Narrator.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;

Directly before the Steps class declaration we need to add the ActionSteps tag. This tells the architecture that this class contains steps.

[ActionSteps]
public class Steps
{
}

The first step we’ll add to the Steps class is a browser step.

[Given("a Firefox browser")]
public void LaunchFirefox()
{
    FeatureContext context = FeatureContext.Current;
    IWebDriver webDriver = new OpenQA.Selenium.Firefox.FirefoxDriver();
    context.Add("browser", webDriver);
}

This method introduces a new concept, the context. NBehave consists of three contexts, a Feature context, a Scenario context, and a Step context. A context is essentially a dictionary whose scope is limited to the element it describes (a step context’s scope is a single step).

This provides two advantages.

  1. A place to store your variables.
  2. If something within your context implements the IDisposable interface, it’s automatically disposed of once the end of the context’s scope is reached.

Next, lets add a step for navigation.

[When("I visit $url")]
public void NavigateTo(string url)
{
    FeatureContext context = FeatureContext.Current;
    IWebDriver webDriver = context.Get<IWebDriver>("browser");
    webDriver.Navigate().GoToUrl(url);
}

The first thing we do in this step, is get the driver from the context. Then we can use the .Navigate().GoToUrl() syntax to navigate the browser to the URL described in the url variable. In NBehave ‘$” annotates that the text following the ‘$’ is a variable name.

Next, the SearchFor step.

[When("I search for \"$text\"")]
public void SearchFor(string text)
{
    FeatureContext context = FeatureContext.Current;
    IWebDriver webDriver = context.Get<IWebDriver>("browser");
    IWebElement textBox = webDriver.FindElement(By.Id("gbqfq"));
    textBox.SendKeys("Hello World!" + Keys.Enter);
}

Finally, the validation step.

[Then("the help button exists")]
public void HelpExists()
{
    FeatureContext context = FeatureContext.Current;
    IWebDriver webDriver = context.Get<IWebDriver>("browser");
    webDriver.WhenThisFails(dr =>
    {
        new WebDriverWait(dr, TimeSpan.FromSeconds(5)).Until<bool>(d =>
        {
            try
            {
                IWebElement helpButton = d.FindElement(By.XPath("//span[@id='fsl']/a"));
                Assert.AreEqual("Help", helpButton.Text);
                return true;
            }
            catch
            {
                return false;
            }
        });
    }).TakeScreenShot();
}

Ninja Level

  • Create a method that gets the WebDriver from the context to avoid redundant code.

Up Next

  • We will implement the NBehave test runner and replace the code in the Google class with a .feature file.