Get in touch
Thank you
We will get back to you as soon as possible

29.4.2014

5 min read

BDD. SpecFlow & Watin. Part II

Meet the second part of BDD tutorial, if you missed the first one, check it out right here To start working with SpecFlow you should set up your IDE. To install SpecFlow plugin in VS go to Tools and open Extension and Updates, load and install SpecFlow and reload IDE. After installation is completed, create Class Library project. Using NuGet install SpecFlow.NUnit. Now specflow files are available and you can add .feature file to the project: BDD Solution Getting Started.png We are ready. Let’s imagine that our Product Owner asked to implement Calculator. Development team made the draft interface of Calculator

public interface ICalculatorCore
{
    double Sum(params double[] values);
    double Minus(double a, double b);
    double Multiply(params double[] values);
    double Divide(double a, double b);
}

Test context is:

public class Calculator
{
    public List<double> Values { get; set; }
    public ICalculatorCore Lets { get; set; }
    public double Result { get; set; }
    public Exception Exception { get; set; }
}

All we should do now is to write very simple test using TestFlow for testing addition of 2 numbers.

@Calculator
Scenario: Add two numbers
    Given I have entered "5" and "6" for calculation
    When I press add
    Then the result should be "11" on the screen

Test contains scenario and implementation’s steps. Steps always start with keywords Given, When, Then. Given – precondition, When — event, that triggers scenario, Then — awaited result. There is also term “And” available and it is duplication of above described construction provided for better readability of scenario. For example: Given I have logged as admin And I have opened a page “My Page” Is equal to Given I have logged as admin Given I have opened a page “My Page” But how are we supposed to translate natural language into syntax construction. To do this you should call context-menu and select Generate Step Definitions; in the shown window, we choose Generate Steps and after that, we get something similar to code shown below.

[Binding]
public class SpecFlowFeatureSteps
{
    [Given(@"I have entered ""(.*)"" and ""(.*)"" for calculation")]
    public void GivenIHaveEnteredAndForCalculation(int p0, int p1)
    {
        ScenarioContext.Current.Pending();
    }
    
    [When(@"I press add")]
    public void WhenIPressAdd()
    {
        ScenarioContext.Current.Pending();
    }
    
    [Then(@"the result should be ""(.*)"" on the screen")]
    public void ThenTheResultShouldBeOnTheScreen(int p0)
    {
        ScenarioContext.Current.Pending();
    }
}

As you’ve probably noticed, Specflow makes use of the special attributes for matching methods with scenarios steps. Possibility of this action is checked with regular expression. Thus we get implemented blocks that can be used in other test scenarios and automated tests that can be constructed from existing blocks by Testers. Only missing functionality-pieces need to be implemented by Developers. “5”, “6” and “11” are parameters for this scenario and they can be changed according to current test case.  Let’s write down now real implementation instead of the dummy one. First of all we are going to create constructor that uses implementation context as argument. SpecFlow supports injection into constructor, that’s why no null reference exception occurs. Scenario steps can be represented as follows:

[Binding]
public class SpecFlowFeatureSteps
{
    private CalculatorContext CalculatorContext { get; set; }
 
    public SpecFlowFeatureSteps(CalculatorContext calculatorContext)
    {
        CalculatorContext = calculatorContext;
    }
 
    [Given(@"I have entered ""(.*)"" and ""(.*)"" for calculation")]
    public void GivenIHaveEnteredAndForCalculation(int a, int b)
    {
        CalculatorContext.Values.Add(a);
        CalculatorContext.Values.Add(b);
    }
    
    [When(@"I press add")]
    public void WhenIPressAdd()
    {
        CalculatorContext.Result = CalculatorContext.Lets.Sum(CalculatorContext.Values.ToArray());
    }
    
    [Then(@"the result should be ""(.*)"" on the screen")]
    public void ThenTheResultShouldBeOnTheScreen(int b)
    {
        Assert.AreEqual(CalculatorContext.Result, b);
    }
}

To start test run call context menu on .feature file and select “Run SpecFlow Scenarios”. Well-known Unit Test Sessions (NUnit) window will be shown with all tests’ results included. Unit test session.png Report contains steps performed by scenario and result. Still SpecFlow is able to do much more. For example it can populate whole structure in one step as  it is described in following case: we need to complete customer’s information with valid data.

Feature: UserSpecFlow
    In order to make possible login
    As a user
    I want to be able to log in
 
@Log in
Scenario: User login
    Given I have entered user data
    | login | password |
    | admin | admin    |
    When I press clicked "login" button
    Then the result I should be logged in

As you can see from example, SpecFlow uses such tables to pass named parameters into scenario. After performing generation, we get following result:

[Binding]
public class UserSpecFlowSteps
{
    [Given(@"I have entered user data")]
    public void GivenIHaveEnteredUserData(Table table)
    {
        ScenarioContext.Current.Pending();
    }
    
    [When(@"I press clicked ""(.*)"" button")]
    public void WhenIPressClickedButton(string p0)
    {
        ScenarioContext.Current.Pending();
    }
    
    [Then(@"the result I should be logged in")]
    public void ThenTheResultIShouldBeLoggedIn()
    {
        ScenarioContext.Current.Pending();
    }
}

Now all data was passed into method using Table. To get their value, call createinstance and use instead of T class to which you data is mapped. In our case it’s User.

[Given(@"I have entered user data")]
public void GivenIHaveEnteredUserData(Table table)
{
    var user = table.CreateInstance<User>();
}

The data list we can get alike.

Scenario: Users data
    Given I have entered list of users
    | login  | password |
    | admin  | admin    |
    | user   | user     |
    | system | system   |
    When I press clicked import button
    Then I should see this data in the table

The only difference is that we are calling CreateSet:

[Given(@"I have entered list of users")]
public void GivenIHaveEnteredListOfUsers(Table table)
{
    var userList = table.CreateSet<User>();
}

We have examined the simplest tests scenarios. But what should we do with testcases that can use different input arguments? We are supposed to use Scenario Outline.

Scenario Outline: Multiply two numbers
    Given I have entered <firstNumber>
    And I have entered <secondNumber>
    When I press = button
    Then result should be <result>
 
Examples: 
| firstNumber | secondNumber | result |
| 3           | 3            | 9      |
| 10          | 2            | 20     |
| 3           | 0            | 0      |

Scenario will execute for each passed case (TestCase analogue in NUnit). Report will include all testcases with their arguments and result of execution. UserSpecFlowFeature.png We should also mention here precondition. Precondition is such a method that will be executed before/after scenario or before/after each testcase. It can be useful for setting initial test context before scenario or testcase. This can be in feature file or in code, with the help of BeforeStep, BeforeFeature, BeforeTestRun, AfterScenario, AfterFeature and other attributes. Here is an example.

[BeforeScenario]
public void CleanUp()
{
}

SpecFlow is powerful instrument for writing tests in BDD development process. It allows you to write test using plain text which will be understandable for other non-technical persons. It is connecting link between business-analytics, users and developers of software product. But it can be used not only for desktop application. In the next article I’ll show you usage example of SpecFlow for Web applications with the help of Watin framework. 1.     http://www.specflow.org/getting-started/ 2.     http://habrahabr.ru/post/182032/

0 Comments
name *
email *