Test-Driven Development: The 3 Rules
As defined by Uncle Bob, the 3 rules of TDD are:
Rule 1 | Rule 2 | Rule 3 |
---|---|---|
You are not allowed to write any production code unless it is to make a failing unit test pass. | You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures. | You are not allowed to write any more production code than is sufficient to pass the one failing unit test. |
Test-Driven Development: The 3 Phases
TDD is therefore a cycle between three phases:
Red | Green | Refactor |
---|---|---|
You have written a failing unit test, and now you must make it pass OR your project can't yet compile, so you need to write enough code to get a minimal program to run | You have written production code to make your latest unit test pass | All of your tests are passing, but your code could be cleaner and more readable, so you make a few adjustments before writing another failing test |
JUnit
We test Java using a tool called JUnit. JUnit is a unit testing framework for Java.
In software, a test is a piece of code that tests other code. If the test "passes", then the code that it is testing is working as expected.
These tests vary in size - some focus on a very specific bit of behavior, and other focus on how all parts of an application integrate together.
A unit test focuses on a very specific piece of functionality.
Project Setup
To implement TDD, you need to be able to write unit tests using JUnit.
You can find easy instructions for quickly setting up a project with JUnit in this We Can Code IT GitHub project:
Once you have set up your project and imported it into Eclipse according to the instructions above, continue to the next slide.
Project Structure
You will notice that your project in Eclipse has several directories:
src/main/java
is where your production code belongssrc/test/java
is where you will put your unit tests
You will start each TDD cycle by writing a failing unit test in src/test/java
, and then you will write production code in src/main/java
to get your test to pass.
Project Requirements
Create a Calculator class that can add, subtract, multiply, and divide numbers. It should also be able to determine the remainder of division.
Each method in the class should accept two integers as arguments and return an integer.
First Test
CalculatorTest
- named after the class it tests
shouldBeAbleToCreateCalculator
Method naming conventions
Run test
We are now in a RED state, as our test code can't compile and run
Going Green
Use Eclipse's suggestion to create calculator class
Make SURE it is in src/main/java and not src/test/java
Re-run test and see that it is PASSING
We are in the GREEN
Refactor
Add a comment to make code easier to understand
"Reduce the cognitive load" - important because YOU have limited brainpower, and that brainpower is best spent solving problems, not digging through messy code
ALSO important because when you work on a team, you need to make it as easy as possible for your team to see what your code is doing. Confusing code is NOT impressive - clean code IS impressive.
Indeed, months down the road when you return to this code it will no longer be familiar to you. You will be grateful you made the code easy for strangers to understand.
RED
Calculator.sum(int addend1, int addend2)
Understanding JUnit Feedback
We have now seen 3 different colors appear in JUnit's feedback to mark specific test methods.
Let's take a look at what each one means:
- Passed Test (green)
- Failed Assertion - a failure (dark blue)
- Java Exception - also a failure (red)
GREEN
Calculator.sum(int addend1, int addend2)
RED
Calculator.difference(int minuend, int subtrahend)
GREEN
Calculator.difference(int minuend, int subtrahend)
RED
Calculator.product(int multiplicand, int multiplier)
GREEN
Calculator.product(int multiplicand, int multiplier)
RED
Calculator.quotient(int dividend, int divisor)
GREEN
Calculator.quotient(int dividend, int divisor)
RED
Calculator.remainder(int dividend, int divisor)
GREEN
Calculator.remainder(int dividend, int divisor)
Helpful Annotations
@Before - use this to set up a class so you can re-use it
@After