BDD, TDD, and Everything Between

Behavior Driven Development, Test Driven Development, and Everything Between

What is TDD (Test Driven Development)

Test Driven Development was introduced by Kent Beck, in 2003.  This followed the concepts of Extreme Programming, introduced in 1999 with a development experiment done by both IBM and Microsoft.
The purpose of the Test Driven Development is to make sure code is clear, tested, and as redundant as possible, by making sure the tests are written first, and code is being added to "fill the blanks".  Every code iteration needs to pass all tests (may those be unit tests, integration tests, data integrity tests, or UI tests).
Writing the tests first allow us to see what fails, how, and allow us to visualize the structure of our code, by making sure each test is performed to test a specific, extremely defined sub-section of a feature.
Let's assume a "BasicMaths" class, to perform simple mathematics operations.
  1. [TestClass]  
  2. public class UnitTest1 {  
  3.     [TestMethod]  
  4.     public void Test_AddMethod() {  
  5.             BasicMaths bm = new BasicMaths();  
  6.             double res = bm.Add(10, 10);  
  7.             Assert.AreEqual(res, 20);  
  8.         }  
  9.         [TestMethod]  
  10.     public void Test_SubstractMethod() {  
  11.             BasicMaths bm = new BasicMaths();  
  12.             double res = bm.Substract(10, 10);  
  13.             Assert.AreEqual(res, 0);  
  14.         }  
  15.         [TestMethod]  
  16.     public void Test_DivideMethod() {  
  17.             BasicMaths bm = new BasicMaths();  
  18.             double res = bm.divide(10, 5);  
  19.             Assert.AreEqual(res, 2);  
  20.         }  
  21.         [TestMethod]  
  22.     public void Test_MultiplyMethod() {  
  23.         BasicMaths bm = new BasicMaths();  
  24.         double res = bm.Multiply(10, 10);  
  25.         Assert.AreEqual(res, 100);  
  26.     }  
  27. }  

As you can see, we have made sure to write our unit tests first, with pre defined values and the expected result to assert.
Obviously, this will throw compilation errors as the class nor the methods exist.  Next, we will create them (using Visual Studio, they can be generated from the unit test).
  1. public class BasicMaths {  
  2.     public double Add(double num1, double num2) {  
  3.         return num1 + num2;  
  4.     }  
  5.     public double Substract(double num1, double num2) {  
  6.         return num1 - num2;  
  7.     }  
  8.     public double divide(double num1, double num2) {  
  9.         return num1 / num2;  
  10.     }  
  11.     public double Multiply(double num1, double num2) {
  12.         return num1 + num2;  
  13.     }  
  14. }  
You may notice one of these methods won't work as expected. This is intentional. Testing for failure is as important as testing for success.
Now that we have our code, we will run the unit test.  It will display an error, saying the Multiply unit test has failed.  This makes sense as it returns 20, and not the expected value: 100.
We should correct it like that:
  1. public double Multiply(double num1, double num2) {
  2.         return num1 * num2;  
  3.     }   

What is BDD (Behavior Driven Development)

Behavior Driven Development assumes requirements may be vague (due to language barriers, a complicated domain, or other).
BDD builds on the Given - When - Then formula:

  • Given a certain scenario
  • When an action takes place
  • Then this should happen
and the Role-Feature-Reason matrix:
  • As a role in the flow
  • I want to perform an action
  • So that the desired outcome will happen
Using libraries such as BDDFY (SpecFlow, or LightBDD) you can create your unit tests before writing your code
  1. [TestClass]  
  2. public class UnitTest1 {  
  3.   private readonly BasicMaths bm = new BasicMaths();
  4.   private readonly Values values = new Values();
  5.     
  6.     [Test]  
  7.     public void ShouldAbleToAddTwoNumbers(decimal p1, decimal p2, decimal result) {  
  8.             this.Given(_ => _.GivenIHaveTwoValues(p1, p2))
  9.             this.When(_ => _.WhenIAddThem())
  10.             this.Then((_=>_.ThenIGetTheExpectedResult(result))
  11.             this.BDDfy();
  12.         }  
  13.  
  14. [Test]  
  15.     public void ShouldAbleToSubtractTwoNumbers(decimal p1, decimal p2, decimal result) {  
  16.             this.Given(_ => _.GivenIHaveTwoValues(p1, p2))
  17.             this.When(_ => _.WhenISubtractThem())
  18.             this.Then((_=>_.ThenIGetTheExpectedResult(result))
  19.             this.BDDfy();
  20.         }  
  21.       
  22.     public void GivenIHaveTwoValues (decimal p1, decimal p2)() {  
  23.             values.Value1 = p1;
  24.             values.Value2 = p2;
  25.         }  
  26.        
  27.     public void WhenIAddThem () {  
  28.             Values.Result = bm.Add(values.Value1, values.Value2);
  29.         }  
  30.         
  31. public void WhenISubtractThem () {  
  32.             Values.Result = bm.Subtract(values.Value1, values.Value2);
  33.         }   

  34.     public void ThenIGetTheExpectedResult(decimal result) {  
  35.         Values.Result.ShouldBe(result);
  36.     }  
  37. }  
  38.  
  39. Scenario Should be able to add two numbers
  40.      Given I have two values 1.2, 2.3
  41.      When I add them
  42.      Then I get the expected result 3.5

  43. Scenario Should be able to subtract two numbers
  44.      Given I have subtract values 5, 3
  45.      When I subtract them
  46.      Then I get the expected result 2

Everything Between (Opinions, and Conclusions)

As you can see, these are two seperate approches towards unit testing, and testing in general,  While it seems like BDD requires a bit more code, this is only due to the nature of the framework, which enables the use of proper English.  The output from the BDD approach will be much more readable.  More over, this approach allows the stakeholders (and/or QA) to create their own scenarios, thus making sure we are testing what is needed, and not what we think id needed.

This approach eliminates the constant wondering of "What should be tested" as it is the QA and business side who decides that.  It does (and will) prevent developers testing built-in methods such as .ToString() or performing tests for cases we're not interested in.

Testing for failure is as important as testing for success

Elad Shalom, M.Sc
Director of R&D at InteliSys Aviation Systems

Comments

  1. thank you.
    https://rozmusic.com

    ReplyDelete
  2. Wow very knowledgeable and informative article. Thanks for sharing this great post. If you Looking for professional and licensed verified Packers and Movers in Hyderabad at an affordable cost?
    Your search ends here... Hire the most licensed and verified packers and movers at an affordable cost and save money, compare the quotes with the top 3 competitors and choose the best one!
    100% Licensed & verified Transporters in India

    ReplyDelete

Post a Comment

Popular posts from this blog

Linked Files in Visual Studio 2010

SEO One On One - Web of Links

Advantages and Disadvantages of Decriminalising Privacy