B D D

Behavior Driven Development



In 360 seconds !




B D D = T D D evolved



Following slides are going to demonstrate that in Java

STARTING T D D

public class TradeTest {
     @Test
      public void checkNoAlertUnderThreshold() {
           TradingServices ts = new TradingServices(...);
            Stock s = ts.createStock("STK", 15.0);
            ts.tradeAt(s, 5);
            Assert.assertEquals("OFF", s.getAlertStatus());
      }
      @Test
      public void checkAlertAboveThreshold() {
           TradingServices ts = new TradingServices(...);
            Stock s = ts.createStock("STK", 4.0);
            ts.tradeAt(s, 5);
            Assert.assertEquals("ON", s.getAlertStatus());
      }
}

T D D going out of control

More use cases => More tests => more code repeated : 
  • construct TradingServices 
    • if constructor change, I need to change everywhere
  • create a stock / trade a stock
    • if signature change, I need to change everywhere
    • if method is moved to another class ...
  • check the status
    • assertEquals("OFF", s.getStatus()) : is that comprehensive testing ?
  • => Refactoring

T D D BACK UNDER CONTROL

public class TradeTest {
     TradeSteps steps = new TradeSteps();
     @Test
      public void checkNoAlertUnderThreshold() {
           steps.givenStock("STK", 5);
           steps.whenTradeStockAt("STK", 4);
           steps.thenAlert("STK", "OFF");
      }
      @Test
      public void checkAlertAboveThreshold() {
           steps.givenStock("STK", 4);
           steps.whenTradeStockAt("STK", 5);
           steps.thenAlert("STK", "ON");
      }
}

T D D BACK UNDER CONTROL

public class TradeSteps {
    TradingServices ts = new TradingServices(...);
     Map<String, Stock> stocks = new Map();

     public void gicenStock(Sting name, Double threshold) {
         stocks.put(name, ts.createStock(name, threshold));
     }
     public void tradeAt(String name, Double value) {
         ts.tradeAt(stocks.get(name), value);
     }
     public void thenAlert(String name, String status) {
         Assert.assertEquals(status, stocks.get(name).getStatus());
     }
}

BENEFITS OF REFACTORING

  • The test are more readable (given / when / then)
    • but not as readable as plain english
  • The steps are reusable accross tests and easy to write
    • provided they are only doing one thing 
    • and instanciating steps is easy
  • if the constructor or method signature change : 
    • no need to change, I just need to write another step and to mock additional parameters
  • BUT what if I need more :
    • HTML reports readable by business analysts
    • What EXACTLY is going wrong : the "given" or the "when" or the "then"

B D D

Scenario : no alert when trade under threshold
Given stock STK with threshold 5.0
When I trade STK at 4.0
Then the alert status of STK is OFF

Scenario : alert when trade above threshold
Given stock STK with threshold 4.0
When I trade STK at 5.0
Then the alert status of STK is ON

B D D

public class TradeSteps {
     @Given("stock $name with threshold $t")
     public void givenStock(String name, Double threshold) {}
     
     @When("I trade $name at $v")
     public void whenTradeStock(String name, Double value) {}
     
     @Then("the alert status of $name is $s")
     public void thenAlert(String name, String status) {}
 }

B D D


B D D benefits OVER T D D

  • Same benfits as T D D
    • agreement on behavior before implementation (but we can agree in plain english)
    • enforce good design (ex : dependency injection)
    • free regression testing
    • free documentation
  • Speaks the business language, not programmer language
    • Better communication
    • Unambiguous for analysts, testers and programmers
    • Easy refactoring of code since scenario do not change

SCALING B D D

  • Use dependency injection from your app


public class StepsFactory {
   DatasourceFactory dsF = new DatasourceFactory(...);
   Factory f = new Factory(dsF);
   public TradeSteps   tradeSteps() {
       return new TradeSteps(f.tradeServices());
    }
}

SCALING B D D

  • Use Continuous Integration
    • The BDD framework must integrate with your CI server
      • ex : jBehave is a jUnit extension
        => native support with all CI servers that support jUnit
        => native support with all CI servers that support java

SCALING B D D

  • Treat Steps as production code (even if they are untested)
    • group steps together but keep classes small
    • ideally, one steps per service
      • CreateStockServices => CreateStockSteps
      •  TradeStockServices => TradeStockSteps

BDD

By Christophe Blin

BDD

What is Bdd ? Why use it ?

  • 2,560