This document discusses Test Driven Development (TDD). TDD involves writing a failing test, writing the code to make it pass, then writing another failing test. This leads to an application design that is leaner and simpler, with interfaces better designed from the user's perspective. Some key points:
- TDD breaks complexity into small, simple steps and thinks from the outside in from the user's point of view.
- TDD results in tests that act as documentation and bugs being identified and fixed sooner.
- Challenges of TDD include it being hard work and some things not lending themselves to automated tests.
2. What Is TDD?
Write a failing Write the code to Write another
test make it pass failing test
3. Unit Tests
describe("Score Calculation Behaviour", function() {
it("should have score 0 at the start of the game", function() {
var game = new BowlingGame();
expect(game.score()).toBe(0);
});
});
4. Thinking From The Outside
In ...
• Consider how the application will be USED
• Other terms for Test Driven Development
• Test First Development
• Test Driven Design
• Acceptance Test Driven Development
• Behaviour Driven Development
5. Acceptance Tests
Feature: Bowling
! In order to play a game of bowling
! As a bowler
! I want to see my score for my turn
! Scenario:
! ! Given there are two players
! ! And the game contains ten frames
! ! When I roll the ball and hit zero skittles
! ! Then my score is displayed as 0
! ! When I roll the ball and hit three skittles
! ! Then my score is displayed as 3
! ! And it is the next player's turn
6. Driving The Application
Design
• Break complexity into small, simple steps
• Think from the outside in
• Leads to a leaner, simpler application design
• Interfaces are better designed from the user’s point of
view
7. Other Benefits Of TDD
• Tests as Documentation
• Bugs are identified and fixed sooner and more quickly
• Code is more modular
• Satisfaction of green tests!
8. Challenges
• It’s HARD!
• Some things don’t lend themselves to automated tests
• Databases
• Networks
• Links to third parties
• Management may not agree
• Maintenance overhead
• Fixation on statistics like Test Coverage
When you think about testing software, it’s not unusual to consider this as an activity that happens at the end, after the app is built. \n\nHowever, many agile teams develop, or try to, in a different way - by writing the tests before writing any code - this is known as test driven development, and the tests are often automated rather than manual. I want to try and explain today this concept in more detail, the advantages and also the challenges. \n
What is TDD?\n\nAt a simple level it is a cycle of creating tests, then writing the code that makes them pass, then creating a new test. But in practice it’s not as simple and there are a lot of implications. \n\n
A lot of the time when we talk about TDD, we talk about the practice of writing unit tests. Unit tests are automated tests, very short pieces of code that test one small thing. \n\nThis is an example of a simple unit test for a piece of JavaScript code for a bowling game. This looks pretty different to unit tests written in Java or Ruby, for example, but the principles are the same. Even if you don’t really understand the code itself, you can see that it’s very short - it just tests one thing. In this case, we set up a bowling game, and test that before we’ve rolled any balls the score is 0.\n\nWe don’t care in this test about strikes, or spares, or even what happens if I actually hit some skittles. \n\nThat’s the purpose of unit tests - to just test one very simple case in the code. \n
However, the practice of driving the development of the application using tests doesn’t only have to mean writing small, specific unit tests. Thinking about the application from the outside, considering how it will be used and writing tests against that, before working on the actual development has huge benefits. \n\nSometimes people use other terms such as Test First Development, to emphasise the fact that tests are written first, or Test Driven Design - since writing the tests first helps drive the design of the application. \n\nThere are also techniques which are adapted from test driven development such as acceptance test driven development and behaviour driven development. These are essentially the same as test driven development, but encourage developers to really focus on the behaviour of the end user or consumer of the application when designing tests. \n
Different types of tests\n\nI already mentioned and showed you an example of a unit test, and in the last slide I mentioned acceptance test driven development. Acceptance tests are a different type of test, which runs against and tests the entire application, using it as a real user would. Acceptance tests may be run manually, or they can be automated. \n\nFor example, you may have heard of or used software such as Selenium or Webdriver, which can run the browser, click links and check for elements on the page without needing human intervention.\n\nNot all applications run in the browser, some might just provide an interface for other applications to use - for example Facebook and twitter provide APIs for other sites. If I’m creating this kind of application, the acceptance tests would just run against my API, using it in the same way that a consumer of the API would. \n\nThis is an example of the steps in an acceptance test - developers can then write code to execute the test, or it could be executed manually. Although this is effectively testing something similar to the previous unit test, the acceptance test is more verbose and also tests more fully the application, with multiple steps to complete a turn and checking for the display of the score, rather than just the calculation. \n\nYou can never fully replace the need for manual testing - when we talk about test driven development, it’s just as valid to start by writing a test case to be executed manually \n
One of the main benefits of writing tests first, regardless of whether they are automated or run manually, is that they can help to drive the application towards a leaner, simpler design compared to writing tests afterwards .\n\nBy focussing on writing a small test and then making it pass, developers can break down the complexity of a large problem into much smaller, simpler things.\n\nIf I had to think about how to implement a web based bowling game, there are many things to consider - how will I display the score? What about calculating the score for strikes and spares - particularly in the last frame? How will I keep track of different players? It doesn’t take very long to become overwhelmed with the problem, and when faced with this, it’s easy for a developer to start reaching for complex solutions or introducing a multitude of third party frameworks before determining if they are really needed. Large, complex solutions are much harder to keep in your head, so I quickly need a document or diagram before I can even start thinking about development. \n\nBy breaking down the problem and just tackling the simplest part of it first, for example the case where I hit no skittles and the score is simply zero, it becomes much easier to just make a start, make the simple case pass and then figure out the next step. I know it’s going to get complicated when I have to consider strikes and spares but for now I don’t need to stress about it. \n\nAdditionally, by writing the test for the first step before starting development, it forces me as a developer to think about how I want to interact with the code, or the application. \n\nBy writing the acceptance test that I showed you for the bowling game, for example, I already know I will need to display a score and update it after each roll, which will give me some direction in how I decide to implement the game. I don’t know yet if I need to store scores, but I do know I need to be able to switch turns to a new player. \n\nWithout a test, I might just dive in and start thinking about the architecture - for example, which database and persistance framework to use, before finding out later that actually the game can exist entirely in the browser without needing to ever store any information. \n\nStarting from the interface for the application, or working from the outside in, means I can really think about what I need on the inside - and build just enough to support the interface, but no more. \n\nIf I start from the inside and start building out, by the time I get to the interface I might find that actually I need to bend it to fit what I’ve already built - after all, I’ve probably got a deadline to meet and no time to go back and change it. \n
Other benefits of writing the tests first\n* Tests become a living document for the code\n* Code has fewer bugs\n* Running the tests constantly ensures that if new bugs are introduced they can be identified and fixed more quickly - while the developer is still writing the feature rather than a long time afterwards when it’s forgotten\n* writing unit tests means taking small steps and validating each one\n* more modularised code - because it has to be testable and we try to only test one thing at a time, it helps prevent us from writing long, complicated code with lots of dependencies because that’s harder to test\n* it’s fun! There’s a certain satisfaction in seeing the tests go from red to green\n\n
Disadvantages and challenges\n* TDD isn’t easy and doesn’t always come naturally\n* Some things are just hard to test - databases for example, because you have to know what data is in there if you are going to test the code that gets it out\n* Some programs run across a network or rely on third parties so you can’t always test the entire thing\n* Management may not always believe that time spent writing tests is time well spent\n* Tests can become maintenance overhead, particularly acceptance tests or badly written tests\n* Slow running tests like browser based tests can be a pain, developers don’t always run them so they can be fragile\n* People can be fixated on statistics such as test coverage (there are ways to measure what percentage of the code is executed by the tests) over writing good quality code and tests - you don’t always need to test every single thing, but that’s a subject for another talk\n
So how do you actually go about driving development using tests?\nI’m going to use a simple example of creating a bowling game to demonstrate two techniques that are often used together, known as red/green/refactor, and ping pong pairing. \n\n\n
Red Green Refactor\n\nOne effective way of test driving the development of applications is to use the red-green-refactor technique. This means we write a tiny bit of the test, enough to make it run but fail, or go red. \n\nThe next step is to write enough code to make the test pass, or turn green. \n\nFinally, we look at what we’ve written and refactor it if necessary. Then we start again by writing another failing test. \n\n(demo)\n
Ping Pong\n\nPing pong pairing takes this technique one stage further. Pairing is a common development practice in Agile teams, which basically means two developers with one computer working on the same piece of code. \n\nIt uses the same principle as red-green-refactor, but developers take it in turns to write a failing test, then make it pass before writing the next failing one. \n\nAdvantages\n* code design is shared, because each developer writes a test each which drives the design of the interface\n* you can also have a bit of fun with this though by strictly following the rule of writing the absolute minimum functionality to make the code actually run\n(demo)\n* Although this may seem a bit daft, the benefit is that it forces both developers to think more thoroughly about what the next test should be\n\n