Software quality guarantee with TDD (Test Driven Development)

One of the big challenges which software development teams face is quality guarantee. In projects which used the traditional Waterfall method, there was a well defined phase in which software tests were carried out. The problem was, it was a late phase and often bulldozed over or ignored completely due to the pressures to deliver on time.

In this article we’ll talk about Test Driven Development (TDD), a software development practice in which the tests are thought out and written down before the business code. With this, we greatly increase the product’s quality and consequently the satisfaction of clients and users.

Can we test at the beginning, before starting the development?

Yes, the quality of the software is everyone’s responsibility. Before development even begins. The Product Owner, Development Team and Scrum Master should work together to ensure that this is done.

For the following topics, we’ll use as an example a Team developing an e-commerce site. During a meeting, the marketing director delivers the following piece of news: on the 15th of this month, we’ll be launching a Christmas offer with the following series of discounts: purchases of under $500 stay the same, no discount. Purchases of over $500 get a 10% discount. The Product Owner now writes the following user story: I, as buyer, want to get a discount to save my Christmas bonus when shopping for gifts.

Acceptance Criteria

For each item on the backlog, the Team must define the acceptance criteria. These criteria define the software behavior expected by the Product Owner, users, clients or other stakeholders. The criteria should be defined before the start of development, in order to prevent later surprises and all those well-known phrases which cause friction between the Development Team and the Product Owner: “You never said that before”, “We didn’t know that”, “You’re not implementing such-and-such” etc.

An interesting format for writing down acceptance criteria was introduced by Dan North in 2006. It solves a few important issues for a good test. What scenario is the user in, what’s his action and what result does he get back? The format is:

Given <Necessary conditions>
When <event>
Then <result>

The acceptance criteria for the story we’re dealing with might be:

Acceptance Criteria 01
Given client makes a purchase of $499.99
When purchase is made
Then no discount is applied And final purchase amount is $499.99

Acceptance Criteria 02
Given client makes a purchase of $500.00
When purchase is made
Then discount of 10% is applied And final purchase amount is now $450.00

Note how the criteria clearly describe to the team what the discount is as written in the user story. In this case, they also help define the value limit of the discount range.

Test Driven Development (TDD)

To demonstrate the workings of TDD let’s create a simple Java project using the Eclipse IDE, not forgetting that TDD is not specific to Java and works with various programming languages.

Preparations

To create a project in Java go to File → New → Java Project. For this example let’s create a project called e-commerce.

To do the TDD, you’ll need to add the JUnit framework in Classpath by right-clicking on your project → Build Path → Add Libraries → JUnit → Choose version 4 or 5.

Let’s put together the following project structure:

TDD-knowledge21-Example folders and application packets.
Example folders and application packets.

Separating things

Don’t mix test code with business code. The test code won’t go to production. So we’ll separate the business classes in the source folder src/main/java and put the test classes in src/test/java.

In our example, during planning, the team has agreed that the functionality which resolves the story will be implemented in the class DiscountCalculator. They’ll create the method called calculate which receives the purchase amount and return the amount with the discount. However, since our team uses TDD, our first creation will be the class DiscountCalculatorTest in the packet br.com.k21.e-commerce.control.test.

TDD-knowledge21-Initial DiscountCalculatorTest
Initial DiscountCalculatorTest

Let’s create the first test with the first acceptance criteria.

TDD-knowledge21-Initial DiscountCalculatorTest

Before we proceed, there are a few important things we should address.

Meaningful names

Unless you’re working in a language with restrictions in name length of names or variables, like Cobol, don’t spare characters in making your declarations. When another programmer takes a look at your test, he or she should understand what the test does, which and what the entry and exit variables are for and what method was invoked.

The test code should be very simple so that it becomes the documentation. If you’re having to add comments to the tests, consider whether the business code is too coupled or there’s too much complexity.

“Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.”
James F. Woods, 1991

Arrange Act Assert (AAA)

The structure of a good test is made up of three well defined parts, known as Arrange, Act and Assert (AAA).

Arrange is the test preparation part. Here we add the variables we’re going to need or any lines of code necessary for the action we want to be tested to be executed.

Act is the action. This is where we invoke the method we want to test in the business class.

Assert is the affirmation, where we verify whether the expected value is identical to the calculated value or not.

Baby Steps

In TDD we also like Baby Steps. If you’re a programmer and have seen the acceptance criteria, you’ve probably already thought of all the conditions necessary for meeting those criteria. You’ll be itching to get on with putting all the IFs in the business class. DON’T!

Take it slowly. In real life contexts, you’ll have far more than three acceptance criteria. Putting everything in the business class at once will mean you’ll lose a sense of what’s already been tested (or not). When you then do tests later, you may end up with a false positive. Something which isn’t working properly, but you weren’t able cover those conditions in a test.

As you can see, a business class hasn’t been created and this is why Eclipse cites an error on line 13. If you run the tests (Right-click on the project → Run As → JUnit Test), you’ll get an error message: java.lang.Error: Unresolved compilation problems: DiscountCalculator cannot be resolved to a type. Meaning that DiscountCalculator doesn’t exist.

TDD-knowledge21-babysteps

Let’s create it. Right-click on the packet br.com.k21.e-commerce.control in the source folder src/main/java → New → Class → Name: DiscountCalculator → Finish.

Go back to the class DiscountCalculatorTest and import DiscountCalculator. Notice that the error has now moved to line 21. If you run these tests again, you’ll get the error: java.lang.Error: Unresolved compilation problem: The method calcular(double) is undefined for the type DiscountCalculator. Which means the calculate method doesn’t yet exist in the calculator.

TDD-knowledge21-babysteps2

Let’s create the calculate method in DiscountCalculator. Initially it’s like this:

TDD-knowledge21-babysteps3

TDD Cycle: Red, Green, Refactor

TDD-knowledge21-tddcycle
Red means that the first time we run the test, it must not pass. It must show an error. This will help you avoid false positives.

Green. You’re going to implement the business code, but only enough for the test to pass. Don’t do anything too grand to start with.

Refactor. The red and green steps create a sort of safety net which guarantee your code contains no unexpected behavior. From now on, we can refactor to clean it up.

Run the test and you’ll get an error message: java.lang.AssertionError: expected:<499.99> but was:<-1.00>. Which means we were expecting one figure and got another. The  error is on line 24 (Assert).

TDD-knowledge21-assert-error

Let’s implement the minimum required for our test to pass.

TDD-knowledge21-8

We’re simply entering the amount of the past purchase as the amount with the discount. Run the tests and they should be green.

TDD-knowledge21-10

Is Refactoring necessary? Not so far. We can go to the next acceptance criteria.

Acceptance Criteria 02

Given client makes a purchase of $500.00
When purchase is made
Then discount of 10% is applied And final purchase amount is now $450.00

Notice that we now have to have a condition. If the purchase is $500.00, then the calculate method should give $450.00. Let’s create the test:

TDD-knowledge21-11

Notice I keep all the tests I’ve created. And that they’re mostly copy/pasted from each other. In running the tests you’ll get 1 green for the previous test and 1 test which isn’t passing and everything will go red.

TDD-knowledge21-12

Once again, we’ll implement the minimum for it to work.

TDD-knowledge21-13

Run the tests. Now we’ll see everything is nice and green.

1, 2, N

What happens if the purchase amount is $600.00. Will it pass? Create a test for it.

TDD-knowledge21-14

Run the tests. It didn’t pass? Why?

TDD-knowledge21-15

Let’s use the 1, 2, N standard. This means we’re only going to change the code enough for the test to pass first time. Just enough to pass the second time. From then on, we create a pattern and can provide a more concrete solution for all cases.

So let’s implement just enough for this test to pass and keep the rest working.

TDD-knowledge21-16

Run the tests and you’ll see everthing’s green.

Now, what if the purchase amount were $739.50? We already have a pattern. It’s time to enter our solution which meets all N cases possible. Let’s create the test.

TDD-knowledge21-17

Run the tests. This last one won’t pass. Let’s alter the business class, but this time in order to serve all cases.

TDD-knowledge21-18

Run the tests and you’ll see we’re successful in all 4.

Refactoring

Wow! This code is ready. Now we’ve created our safety net and our code is passing all the tests, we can refactor it. We’re going to use some practices of Clean Code and take out the magic numbers and unnecessary spaces.

TDD-knowledge21-19

Always execute the tests after refactoring to see whether everything is still working.

TDD-knowledge21-20

Summary of the article

In this article we’ve been talking about Test Driven Development (TDD). We’ve taught you the Red, Green, Refactor cycle which should be used during the entire development process; the structure of a good test code with Arrange, Act and Assert; why you should proceed with baby steps; and the 1, 2, N strategy for creating business code. We also demystified the idea that refactoring is a bad thing. In fact, it’s desirable when we’re improving the quality of our source code.

If you got this far, you already know the basics of TDD. You can stop reading and start practicing. I’m just going to add two situations which often come up and which it would be good for you to know how to handle.

Want to know more about this topic?

The code for this article is available on GitHub at link https://github.com/avelinoferreiragf/K21-CSD

 If you want to go further and learn how to do Continuous Delivery, do our training for Certified Scrum Developer (CSD).

See another related post:

Automated test? Where do I start?

And read the books cited in this article:

Epilogue Part I: The boss has gone mad!

The boss went crazy. The special offer worked so well that now he wants to give a 20% discount for purchases of more than $1,000.00.

The Product Owner has created the user story: I, as buyer, wish to get a discount of 20% on purchases of over $1,000.00, since I have to buy many gifts and I want to keeping saving my Christmas bonus.

Acceptance Criteria 03.
Given client makes a purchase of $1,000.00
When purchase is made
Then discount of 20% is applied And final purchase amount is now $800.00.
What do we do first? …. That’s right, the test which validates this condition.

TDD-knowledge21-21

Run the tests and we’ll get an error message saying the acceptance criteria hasn’t been met.

Let’s implement just the minimum for it to keep running.

TDD-knowledge21-22

Create the tests you consider relevant. Mainly testing limit values such as $999.99. Refactor when you’ve created your safety net and everything is green.

 TDD-knowledge21-23

Epilogue Part II: The careless programmer.

The marketing team wants to stimulate impulse buying and wants to do an experiment, giving a 5% discount for purchases under $500.00. The PO has created a new user story: I, in marketing, wish to stimulate impulse buying by offering a 5% discount so that my turnover increases by 20%. The PO, together with the team, makes the following acceptance criteria:

Acceptance Criteria 04
Given client makes a purchase of $499.99
When purchase is made
Then discount of 5% is applied 5% And final purchase amount is now $474.99.

The problem is that a new programmer has joined the team and he hasn’t done Automated test with Agile Practices nor Certified Scrum Developer. And he hasn’t read this great article. The first thing he did was change the business code.

TDD-knowledge21-24

What’s going to happen? If you use a continuous integration server such as Jenkins, it will automatically execute all tests whenever it detects alterations in its version control repository (Subversion or Git, for example). It’ll give an error message, because a business rule was altered but not the tests.

TDD-knowledge21-25

Authors

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

We use cookies to ensure that we give you the best experience on our website. If you continue to use this site we will assume that you are happy with it. Please notice that we do not keep any personal information though.