Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to use a unit test vs a BDD test

Based on some light reading about BDD, I came to the conclusion that unit tests are good tools for testing some granular part of your application, while BDD are higher level, where you are able to exercise feature workflows.

Some of the items I would consider candidates for unit tests: sorting algorithms, state reducers, geometric calculations, etc...

Items I would consider candidates for BDD would be feature workflows: adding an item to a cart, logging in, searching the contents of a site to find course material, etc...

I was asked by a client to write a sorting algorithm, and normally I would have just written a unit test like:

public class SorterTest
{
    [TestMethod]
    public void TestSort()
    {
        var numbers = new List<int>() { 9, 8, 7, 6, 5, 4, 3, 2, 1 };
        var expected = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

        var sorter = new Sorter()
        {
            Input = numbers
        };

        var sorted = sorter.Sort();
        CollectionAssert.AreEqual(expected, sorted.ToList());
    }
}

However, the client asked for a BDD test, so I came up with the following:

Feature: Sort
In order to to ensure sorting of integers
I want to be able to sort a collection of integers

Scenario Outline: Perform a sort
Given I have these integers: '<input>'
When I sort them
Then the result should be '<expected>'

Examples:
| input                           | expected          |
| 9,8,7,6,5,4,3,2,1               | 1,2,3,4,5,6,7,8,9 |

To make my Sorter object testable for BDD, I had to alter it:

    private Sorter sorter = new Sorter();
    [Given(@"^I have the following integers: '(.*)'$")]
    public void GivenIHaveTheFollowingIntegers(string numbers)
    {            
        var inputs = numbers.Split(',')
                            .Select(s => Convert.ToInt32(s.Trim()))
                            .ToList();
        sorter.Input = inputs;
    }

Note for setting up the test with Given, I had to add an Input property to Sorter tp prepare for the sort. The downside is that in application code, if I want my sorter to perform a sort, I will always need to set this property prior to performing the sort:

sorter.Input = intCollection;
var result = sorter.sort();

I'd rather just have:

var result = sorter.sort(intCollection);

Is BDD appropriate for this sort of test? If so, am I doing it right? Adding the Input property feels wrong, should I be doing it some other way?

If not appropriate, how does one go about drawing the line between BDD and unit tests? There is an existing SO post, but the answers reference a book. It would be nice to get some better guidance.

like image 726
Mister Epic Avatar asked Dec 14 '22 23:12

Mister Epic


2 Answers

Choosing between both styles

Choosing between the unit test (TDD) approach and the BDD approach boils down to preference. If the customer asks for BDD, provide BDD. If the team is more comfortable to TDD, use TDD.

Mixing both approaches

The choice is not exclusive. I have experience with mixing both approaches. To answer the question where to draw a line between both approaches the Agile Testing Quadrants are quite helpful:

Agile Testing Quadrants Picture

Drawing a line

I found that the unit test (TDD) approach was more helpful for the technology facing tests. The BDD approach was more helpful for the business facing tests.

Details about this observation

It is more natural to map business requirements to BDD style tests. To test business requirements with some tactile business value, it usually requires the integration of multiple classes. In my experience those BDD style tests were usually integration tests and had the characteristics of functional tests and user acceptance tests.

On the other side the TDD tests were written by programmers for programmers. Many programmers are more comfortable or have more experience with the unit test (TDD) approach. These tests usually tested classes in isolation and with an emphasize on edge cases and technical aspects of the system.

Your provided example is quite an exception because it maps a business case in a single class. In this case both approaches are OK.

like image 169
Theo Lenndorff Avatar answered Dec 22 '22 22:12

Theo Lenndorff


I would have written a unit test.

You mentionned

Some of the items I would consider candidates for unit tests: sorting algorithms, state reducers, geometric calculations, etc...

Items I would consider candidates for BDD would be feature workflows: adding an item to a cart, logging in, searching the contents of a site to find course material, etc...

and I agree with that. Behavior Driven test are good at "speccing" quickly and making sure that the application behaves properly, and does what it is supposed to. You could take this definition and say that you are making sure that your sort works, but the key (IMO) is that you are not testing how your program works, but how an algorithm works, a very specific element of the program. If you look at how you built the test, you see that you are trying to mimic a unit test: you throw a bunch of input, and you compare with the output you are trying to get.

A BDD test aims to test a feature, as scenarios. For instance, if you had a program that organizes phone numbers from a file, sorting would be a part of the process, but you don't cover "sorting" by itself, you test the general behavior of the program (if you get the expected file resulting from executing the application). You setup the test, execute it, verify the result.

You should also write test before you write the code, and I agree with @Theo Lenndoff, having the input as a property is weird and very counter intuitive. (You mentionned it too).

like image 35
Samuel Yvon Avatar answered Dec 22 '22 22:12

Samuel Yvon