Putting BDD in practice using Scala
Tags: bdd, tdd, scala, testing
Published: 2018-09-08 19:22:51

---
title: Putting BDD in practice using Scala
published: true
tags: BDD, TDD, Scala, Testing 
canonical_url: https://www.codacy.com/blog/putting-bdd-in-practice-using-scala/
---

This article aims to give a brief explanation about what Behaviour-Driven Development (BDD) is and how it can be used to fill the information gap between stakeholders and development teams, ensuring everyone (technical or not) is involved in the project’s progress.

The first time I had contact with this approach of software development was a few years ago during a Massive Open Online Course (MOOC) and, I must warn you, I've **never** applied it in production. Nevertheless, I'm fully aware [this is not the holy grail and it might not work so well as advertised][1] but, the idea of having everyone in the team (not only developers) collaborating on the development process, sounded very romantic to me. 
 
 In the past few months, I've been working with Scala and I've been thinking about what would be the best way to apply BDD using the most popular testing tools in the Scala environment (ScalaTest and Specs2) and the most popular BDD tool: Cucumber. In this article I will compare these three tools using a very simple example.

### What's BDD?

BDD can be described, in very simplistic terms, as an extension for [Test-Driven Development][2] (**TDD**). 
The typical TDD cycle, aka [Red/Green/Refactor][3], starts with the development of a failing test. The next step is to write the code to make this test pass and finally, refactor the code in order to improve its readability and maintainability.

![Typical TDD cycle](https://thepracticaldev.s3.amazonaws.com/i/c73l2lmwkewewlcqe4u2.png)

In comparison, the BDD cycle is almost identical to the latter except the first step is replaced by the writing of a specification, rather than the development of a test. Meaning this first step can be done by a non-technical person, for instance a member of the business team or a functional analyst with an understanding of how the system should behave. The best part is this specification can be used for tests automation in the development process.   

![Typical BDD cycle](https://thepracticaldev.s3.amazonaws.com/i/29qwvbtqg32bewrk3dwq.png)

BDD can also be described as the intersection between [Domain-Driven Design (**DDD**)][4] and TDD.
Domain-Driven design is an approach to software development aiming to deal with projects with complex business rules where collaboration between developers and domain experts (typically, this role is assumed by the client) is necessary to set a common language and a domain model that can be translated into very specific and detailed requirements. In BDD, this specific requirements are used to drive the development process in the first step of its cycle. 

![DDD, BDD and TDD](https://thepracticaldev.s3.amazonaws.com/i/nzcgl8hi4ovn5uf9urnr.png)


### BDD in practice

Obviously, BDD can be put into practice without using a specific tool or framework, you just have to get people to **collaborate**, **record** that collaboration in some form of specification, and then **automate** that specification to drive out the implementation. However, there are tools that can help significantly in the automation process, so we don't have to find a new way to extract values from the requirements to automate tests.


#### Cucumber

[Cucumber][5] is probably the most popular tool when it comes to BDD and it was the first one I worked with. It is advertised as being capable to ease tests automation using executable specifications and therefore, generating living documentation that is easily understood by everyone.  

[![Cucumber Venn Diagram](https://thepracticaldev.s3.amazonaws.com/i/zdn7s9klg7a1vlvddjjr.png)][6]

It was originally developed in Ruby, but it is also [officially available][7] for Java and Javascript. 

The specifications are written in [Gherkin][6], a simple set of grammar rules generating plain text structured enough for Cucumber to understand. Here's a simple example of a Bank Account feature specification from where the holder can withdraw cash:

```gherkin
Feature: Account Holder withdraws cash

  Scenario: The account has sufficient funds
    Given an account with a balance of $100.5
    When the Account Holder requests $20.25
    Then the account balance should be $80.25

  Scenario: The account has insufficient funds
    Given an account with a balance of $80.25
    When the Account Holder requests $100.75
    Then the Account Holder should be notified that overdrafts are not permitted
```

Gherkin documents are stored in `.feature` text files and it supports [different dialects][8], so you can even write your specifications in Esperanto! 

The Scala implementation for the previous feature is something like this:

```scala
class BankAccount(val balance: Double) {

  def debit(amount: Double): Either[String, BankAccount] = {
    if (amount < 0.0) {
      Left(s"The debit amount must be > 0.0")
    } else if (balance - amount < 0.0) {
      Left("Overdrafts are not permitted")
    } else {
      Right(new BankAccount(balance - amount))
    }
  }

  def credit(amount: Double): Either[String, BankAccount] = {
    if (amount < 0.0) {
      Left("The credit amount must be > 0.0")
    } else {
      Right(new BankAccount(balance + amount))
    }
  }
}
```

I believe the code doesn't require any detailed explanation. It basically enables the creation of a `BankAccount` instance with a given amount of cash that can be debited and credited, but overdrafts are forbidden.


Using [this library][9] I managed to automate the execution of the previous feature in a Scala project. It looks like this:

```scala
class StepDefinitions extends ScalaDsl with EN {

  var bankAccount: BankAccount = _
  var result: Either[String, BankAccount] = _

  Given("""^an account with a balance of \$(.+)$""") { (balance: Double) =>
    bankAccount = new BankAccount(balance)
  }

  When("""^the Account Holder requests \$(.+)$""") { (amount: Double) =>
    result = bankAccount.debit(amount)
  }

  Then("""^the account balance should be \$(.+)$""") { (expectedResult: Double) =>
    assert(result.right.exists(_.balance == expectedResult))
  }

  Then("""^the Account Holder should be notified that overdrafts are not permitted$""") { () =>
    assert(result.left.exists(_.equalsIgnoreCase("overdrafts are not permitted")))
  }
}
``` 

Basically, we're just extracting the values from each one of the specification sentences and doing operations and assertions with those values.

As you might have noticed ([codacy obviously noticed it][10]), I had to sacrifice [immutability][11], one of the building blocks of [functional programming][12], by using two mutable variables to be able to do the assertions in the last steps. It would be cleaner if each step could return a value to be used in the next steps and do the assertions in the end.
I wasn't the only one that noticed this limitation and some [issues][13] were already reported to make this more functional. However, it seems these improvements won't happen in the near future as it was announced the [support for JVM languages was dropped][14], and this obviously includes Scala.


#### ScalaTest

[ScalaTest][15] is probably the most popular testing tool in the Scala ecosystem. It provides some traits facilitating BDD style, enabling a more grammatical structure in order to write tests as specifications. 

The tests for the BankAccount implementation previously shown looks like this:

```scala
class BankAccountSpecs extends FeatureSpec with GivenWhenThen {

  feature("Account Holder withdraws cash") {
    scenario("The account has sufficient funds") {
      val balance = 100.5
      Given(s"""an account with a balance of $$$balance""")
      val bankAccount = new BankAccount(balance)
      val withdrawAmount = 20.25
      When(s"the Account Holder requests $$$withdrawAmount")
      val resultBankAccount = bankAccount.debit(withdrawAmount)
      val expectedBalance = 80.25
      Then(s"the account balance should be $$$expectedBalance")
      assert(resultBankAccount.right.exists(_.balance == expectedBalance))
    }

    scenario("The account has insufficient funds") {
      val balance = 80.25
      Given(s"""an account with a balance of $$$balance""")
      val bankAccount = new BankAccount(balance)
      val withdrawAmount = 100.75
      When(s"the Account Holder requests $$$withdrawAmount")
      val resultBankAccount = bankAccount.debit(withdrawAmount)
      val expectedMessage = "overdrafts are not permitted"
      Then(s"the Account Holder should be notified that $expectedMessage")
      assert(resultBankAccount.left.exists(_.equalsIgnoreCase(expectedMessage)))
    }
  }
}
```

The main difference I noticed when using ScalaTest compared with Cucumber is the specification text is inlined with the code and the Given/When/Then statements are basically just printed logs to the console, giving a greater level of granularity than just the test names. Therefore, the advantage of having the specification completely separated from the code is lost.


#### Specs2

[Specs2][16] is another popular testing tool in the Scala ecosystem and it's my favourite one. It also provides a [trait][17] enabling the BDD style of using specifications for tests and, in my opinion, this is the best option to practice BDD in Scala. Here's the code for the successfull withdraw scenario:

```scala
class BankAccountWithSufficientFunds extends Specification with GWT {

  def is =
    s2"""
     Given an account with a balance of $$100.5     ${bankAccount.start}
     When the Account Holder requests $$20.25
     Then the account balance should be $$80.25     ${bankAccount.end}
    """

  private val bankAccount =
    Scenario("bankAccount")
      .given(aBankAccount)
      .when(aDouble) { case debit :: bAccount :: _ => bAccount.debit(debit) }
      .andThen(aDouble) {
        case expectedBalance :: bAccount :: _ =>
          bAccount.right.exists(_.balance == expectedBalance)
      }
}
```

As it is visible, the specification text is also not completely separated from the code but it is not inlined. It is in a separated string that could be easily extracted from some other place. In this string, we have to specify where the scenario starts, where it ends, and every line between these two points must correspond to a `given`, `when` or `andThen` call on the scenario.

We also have to write some custom step parsers for the balance and withdraw values and to instantiate the `BankAccount` when the values are extracted from the specification string. Here's the code:

```scala
object CustomStepParsers extends StepParsers {

  private val regex = ".*\\$(.*)$"

  def aDouble: StepParser[Double] = readAs(regex).and((s: String) => s.toDouble)

  def aBankAccount: StepParser[BankAccount] =
    readAs(regex).and((s: String) => new BankAccount(s.toDouble))

}
```

And finally, the code for an unsuccessfull withdraw operation, where the account holder is notified overdrafts are not permitted:

```scala
class BankAccountWithInsufficientFunds extends Specification with GWT {

  def is =
    s2"""
     Given an account with a balance of $$80.25                                        ${bankAccount.start}
     When the Account Holder requests $$100.75
     Then the Account Holder should be notified that overdrafts are not permitted      ${bankAccount.end}
    """

  private val bankAccount =
    Scenario("A Bank Account with Insufficient Funds")
      .given(aBankAccount)
      .when(aDouble) { case debit :: bAccount :: _ => bAccount.debit(debit) }
      .andThen() {
        case insufficientFunds :: bAccount :: _ =>
          bAccount.left.exists(msg => insufficientFunds.toLowerCase.contains(msg.toLowerCase))
      }
}
```

### Conclusion
I think BDD, if correctly applied and with **everyone** commited, can be a very efficient approach to software development. Especially for projects where a significant number of team members don't have a deep understanding of the business logic. Having a way to formally specify what the system should do and how it should behave and use this specification for testing and documentation, can avoid many issues during the development process.  
About the tools comparison, despite Cucumber being the standard tool to practice BDD, I think Specs2 was definetely the best testing tool for this simple example in Scala.

The project with the code used for this blog post is available in [GitHub][18], feel free to check it out.


_See the original article [here][19]_

[1]: https://dzone.com/articles/how-completely-fail-bdd
[2]: https://en.wikipedia.org/wiki/Test-driven_development
[3]: https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html
[4]: https://en.wikipedia.org/wiki/Domain-driven_design
[5]: https://cucumber.io/
[6]: https://cucumber.io/docs/gherkin/reference/
[7]: https://cucumber.io/docs/tools/
[8]: https://cucumber.io/docs/gherkin/reference/#gherkin-dialects
[9]: https://github.com/lewismj/cucumber
[10]: https://app.codacy.com/gh/bmbferreira/BddScalaTestVsCucumberVsSpecs2/issues/index
[11]: https://www.ibm.com/developerworks/library/j-ft4/index.html
[12]: http://en.wikipedia.org/wiki/Functional_programming
[13]: https://github.com/cucumber/cucumber/issues/214
[14]: https://cucumber.io/blog/2018/05/19/cucumber-jvm-languages-support
[15]: http://www.scalatest.org/
[16]: http://etorreborre.github.io/specs2/
[17]: https://etorreborre.github.io/specs2/guide/SPECS2-3.6.6/org.specs2.guide.GivenWhenThenStyle.html#full-support
[18]: https://github.com/bmbferreira/BddScalaTestVsCucumberVsSpecs2
[19]: https://www.codacy.com/blog/putting-bdd-in-practice-using-scala/