Testing. It often gets left to the last minute, then cut because you’re out of time, over-budget, or whatever else.
Management wonders why developers can’t just “get it right the first time”, and developers (especially on large systems) can be taken off-guard when different stakeholders describe different parts of the system, like the story of the blind men describing an elephant.
It’s inevitable, however, that the first step in every project is a discussion about the behaviors of the software or feature to be built. A client or business person comes up to someone on the development team and explains what they want.
Sometimes these interactions come in form of an Agile user story. Sometimes they come in the form of design documents, as in Chris Fox’s blog entry last year. They could also come as flowcharts or mockups in Keynote, or even hurried phone calls.
From these communications alone, a developer is responsible for constructing a system that “just works”.
Why does testing get cut?
There’s an obvious gap here: if the business owner has envisioned the system’s behaviors at the start, why is testing that these behaviors actually work often the step that gets cut?
The answer may be blindingly simple: tests are often not seen as shared capital; they are not thought of as having value to the project, because “they’re just for the engineers”, or similarly, providing value to a single one department or group of people.
How do we make tests this shared capital, this list of system behaviors? By embracing not only test-driven development (TDD), but behavior-driven development (BDD).
What is BDD?
Behavior-driven development should be focused on the business behaviors your code is implementing: the “why” behind the code. It supports a team-centric (especially cross-functional) workflow.
I’ve seen BDD work really well when a developer and either the Agile product owner or a business analyst sit down together and write pending specs (to be filled in later by the developer) in a plain text editor:
- The business person specifies behaviors they want to see in the system.
- The developer asks questions based on their understanding of the system, while also writing down additional behaviors needed from a development perspective.
Ideally, both parties can refer to the list of current system behaviors to see if this new feature will break existing features.
I’ve found this simple act gives me enough constraints that I’m able to think like a developer: “given that I have to implement these tests, how does that constrain me/everyone into specification I can implement in code”? Since they are pending specs, they’re fast and easy to write in the thick of collaboration.
This collaborative approach lets me focus on what the feature provides for the end user, and having the business person right there constrains me to talk about behavior, not implementation.
Let’s see an example
The scenario: You’re a developer on a team responsible for the company accounting system, implemented in Rails. One day, a business person asks you to implement a reminder system to remind clients of their pending invoices. Because you’re practicing BDD, you sit down with that business person and start defining behaviors.
You open your text editor and start creating pending specs for the behaviors the business user wants:
it "adds a reminder date when an invoice is created" it "sends an email to the invoice's account's primary contact after the reminder date has passed" it "marks that the user has read the email in the invoice"
This focus on behavior makes the test useful as verification that you’re building the right feature, not just that your code is correct. Note that the phrasing is in business language, not in the system’s internal implementation language. You don’t see or care that an invoice
belongs_to an account, because nobody outside the development team cares about that.
Some developers prefer to write test cases on the spot, calling methods in the system, setting up expectations, like so:
it "adds a reminder date when an invoice is created" do current_invoice = create :invoice current_invoice.reminder_date.should == 20.days.from_now end
The test suite will fail because we’re yet to write the code to set the
Vis-à-vis failing specs
I understand developers that write failing specs, but with the business person by my side, this has never worked for me. The wrong business person will either get distracted by the details or take this new knowledge and try to micromanage things that the developer knows more about (proper database design, code reuse).
In my experience, writing more than a one-line overview of a specific behavior will bore the business person. They’ll view it as a poor use of their time or grow anxious to describe the next behavior while it’s on their mind.
How does BDD differ from TDD?
Let’s look at this a different way, with a TDD approach, and write out pending tests:
it "after_create an Invoice sets a reminder date to be creation + 20 business days" it "Account#primary_payment_contact returns the current payment contact or the client project manager" it "InvoiceChecker#mailer finds invoices that are overdue and sends the email"
These tests are helpful, but only helpful to one group of people: engineers. BDD is useful for communicating with every member of a cross-functional product team.
You can certainly do test-first development while in a BDD mindset through the use of pending behaviors. First, write the test; then run it (red); then make it work (green); then make it right (refactor).
A lot of work in the BDD community has gone into making the assertion checks inside the test read like English. Here’s a stereotypical RSpec test:
a = 42 a.should == 42
This format makes things easier to read later on. But remember this is not what we’re doing here—the point is to get behaviors down as fast as possible—and enforce the principle of ‘one tested behavior per spec’. Ideally, the pending spec title should tell you what you’re testing.
BDD isn’t about fancy ways to validate your results; it’s about sharing expected behaviors across all members of the team.
Behavior-driven development is about collaboration & communication
Let’s step back into our scenario: working on the company accounting system.
You walk going through the item’s functionality with the business person, with you analyzing the system through its internals (how the objects fit together internally), and them analyzing the system from the outside.
You think of some conditions and ask the business analyst what happens in the following scenarios:
* What's the default reminder date going to be? How many days before the invoice due date? * Are those business days or just calendar days? * What happens if there's not a primary contact associated with the account?
And you get answers. It’s important that the business person understand that you’re not trying to punch holes in their pet idea, or being overly pedantic.
In this way, BDD is a tool to aid collaboration and start a conversation between the two departments. It’s also a way to clarify the scope of a desired feature and get better estimates from the dev team. Perhaps you realize that there’s no way to calculate 10 business days from a given point in time; that’s an additional, separate, feature you’ll need to implement.
Developers will have developer considerations (“What exactly do you mean when you say, ‘day’?”), while business people will have their own considerations (“Please don’t use the term overdue here, that means something different”). Having one group or the other go off and try to write these business logic behavior tests themselves (the promise of Cucumber) cuts out each side’s valuable input.
It’s also a good stand-in for when the Agile Client isn’t physically in the room anymore: to have their desires on paper, mixed with developer analysis (and translation) of what you’re building.
An earlier Toptal Blog post Chris Fox talks about design documents, especially at the beginning of a project. Understanding and extracting the business behaviors scales down from projects where the system is somewhat knowable, to those that require decades of programmer-years to accomplish and have hundreds or thousands of behavioral specs.
Chris’s article also mentions on-screen behaviors of elements, and this is an area where I’m consistently at odds with designers: “What does this button look like when it’s dim” or, “How does this look on 11” screens, because this page is obviously designed for 24” screens?” This back-and-forth with the business person can find gaps in the graphics assets of a project or layout of a page.
In very large cross-functional teams, there are many team members with their own concerns: designers, developers, potentially someone from operations, the database administrator—perhaps QA people (yes, there’s a place for everyone in TDD and BDD!), each with their own concerns and questions. BDD can drive this collaboration. From “what happens when the data is too big for this table?” to, “Hmmm, that’ll be an expensive query, we’ll want to cache that somehow” to, “Wait, should the user see all of that confidential information?”, there may be more at stake than just a developer and a business analyst with questions about this new feature
Behavior-driven development is about shared artifacts
What is a shared artifact?
I like to think of “artifacts” in software engineering as potentially physical things that describe the project or the project team, and which are findable six months down the line. Good artifacts explain why things are the way they are.
Hallway conversations aren’t artifacts. Nor are whiteboard drawings. Whiteboard drawings that get turned into big long class documentations or design documents—these are artifacts. Minutes from meetings are artifacts too.
An artifact is some source code saved to a repository or shared space, and tickets in the ticket system, or notes on the internal Wiki—or even persistent chat logs.
Shared artifacts are, in my mind, the best artifacts. They show not just why something is the way it is, but why it exists in the app at all.
How do you use them?
Behaviors should be a shared team artifact—tests should not just be busy-work for programmers! While it’s best to have a system in which the entire team can easily view the current specs (perhaps the deployment system also extracts and saves the list of behaviors to a private area of the site or a wiki), you could also do it manually every sprint.
The name of the game is “help developers create the specs we need to deliver business value quicker, collaborate interdepartmentally, and make better estimates”.
This company-wide understanding of what the system does is a form of capital too. It’s the “why” to the code’s “how”.
How do we solve the problem of buggy software being delivered to customers? By making sure that testing is not seen as something “only the developers care about”.
Describing and understanding the needs of a system has a ton of benefits beyond code correctness: establishing inter-departmental dialog and making sure all stakeholders’ concerns are met (and not just the stakeholders with big fancy titles). Understanding these needs from the start and testing external business behaviors that the entire team cares about—this a great way to ensure a quality project.