Sunday, July 13, 2014

Exploring BDDfy

BDDfy is a BDD library (part of the larger TestStack project). It's function is to turn regular unit tests to BDD style tests (using the Gherkin syntax). You can read more about BDDfy here.

BDDfy can be used with any test framework or runner.

Acquiring

To add BDDfy to your test project via NuGet run this command in the Package Manage Console.

Install-Package TestStack.BDDfy

Optionally, you can also install code samples with this package.

Install-Package TestStack.BDDfy.Samples

Exploring


Hello world

For this part I'll be using the xUnit test framework. Let's start with something simple.

public class FirstTest
{
    void GivenTwoAndTwo()
    {
        // ...
    }

    void WhenIAddThem()
    {
        // ...
    }

    void ThenTheAnwserShouldBe4()
    {
        // ...
    }

    [Fact]
    public void ExecuteFirstTest()
    {
        this.BDDfy();
    }
}

All the magic is done by the BDDFy extension methods. This will scan the FirstTest class for methods starting with keywords like Given, When and Then. Next, it will run the methods in order (BDD style). Finally, we will get a nice output report like this.

Test output in Visual Studio

BDDfy will also generate a BDDfy.html file in the test project output folder. This is the report of all BDDFyed tests.

HTML tests report

Using attributes to customize the test

BDDfy follow conventions when scanning a class for methods of interest, you can find a list here. If we need more control we can do it by using attributes.

[Story(
    AsA = "As someone lazy",
    IWant = "I want the computer to add 2 number",
    SoThat = "I don't have to do the math myself")]
public class TestWithAttributesToOverriteText
{
    [Given("Given 2 + 2")]
    void GivenTwoAndTwo()
    {
        // ...
    }

    [When(" + ")]
    void WhenIAddThem()
    {
        // ...
    }

    [Then("Then the anwser = 4")]
    void ThenTheAnwserShouldBe4()
    {
        // ...
    }

    void AndThenItShouldDisplayTheAnwser()
    {
    }

    [Fact]
    public void ExecuteTestWithAttributes()
    {
        this.BDDfy();
    }
}

First, the [Story] attribute allow us to provide the classic story definition for the test. Other attributes like [Given], [When] and [Then] allow us to provide a custom description for the steps. Also, using the attributes will allow us to name the step methods the way we want.

Creating more than one scenario per story

Usually a story contains more than one test or scenario. We can do this using nested classes.

[Story(
    Title = "Using story attribute and setting the Title!",
    AsA = "As someone learning BDDfy",
    IWant = "I want to try splitting scenario in separated classes",
    SoThat = "My code is cleaner")]
public class TestWithStoryAndScenarioInSeparatedClasses
{
    [Fact]
    public void FirstScenario()
    {
        new S1().BDDfy("Custom scenario title");
    }

    [Fact]
    public void SecondScenario()
    {
        new S2().BDDfy();
    }

    private class S1
    {
        void GivenWhatever() { // ... }
        void WhenSomethingHappens() { // ... }
        void ThenProfit() { // ... }
    }
    private class S2
    {
        void GivenWhatever() { // ... }
        void WhenSomethingElseHappens() { // ... }
        void ThenProfit() { // ... }
    }
}

This will group scenarios together in the output report.

Using the fluent API for even more control

With what we've seen previously we need to create a new test class for each scenario we have. That leads to a lot of duplicated code unless we delegate to a common test fixture. An alternative is to use BDDfy fluent API to get some code reuse between our scenarios.

public class TestWithFluentApi
{
    [Fact]
    public void ReusingStepForScenario1()
    {
        new TestWithFluentApi()
            .Given(s => s.GivenWhatever(), "Given some pre-condition")
                .And(s => s.AndOtherGiven(54))
            .When(s => s.WhenSomethingElseHappens())
            .Then(s => s.ThenProfit())
                .And(s => s.AndManyMore(45))
            .BDDfy();
    }

    [Fact]
    public void ReusingStepForScenario2()
    {
        new TestWithFluentApi()
            .Given(s => s.GivenWhatever(), "Given some pre-condition")
                .And(s => s.AndOtherGiven(123))
            .When(s => s.WhenSomethingElseHappens())
            .Then(s => s.ThenProfit())
                .And(s => s.AndManyMore(321), "And {0} more things!")
            .BDDfy("Scenario 2 with steps re-use");
    }

    void GivenWhatever() { // ... }
    void AndOtherGiven(int input) { // ... }
    void WhenSomethingElseHappens() { // ... }
    void ThenProfit() { // ... }
    void AndManyMore(int expected) { // ... }
}

With this style of test, not only we can reuse steps between scenarios we also gain the ability to parameterize the steps.

Assessment

I've only scratched the surface of what BDDfy can do. You can read more about on BDDfy usage and customization on the project web site.

In the past I've use SpecFlow for my BDD tests. With SpecFlow you write your specification in a text file using the Gherkin language. The tool then parse the file and execute corresponding method for each steps. Having a text file seems interesting because we could have a business analyst write those. In really, developers ends up writing the stories and scenarios anyway.

This is why I like BDDfy, it's easy to learn and gives developers a lot of control over the way we create BDD style tests.

One more for my toolbox!

I hope you enjoyed this introduction to BDDfy.

No comments: