Jump-starting with TDD
Test-driven development (TDD) is one of the most desirable skills among software developers today and rumor is that within a few years developers without this skill will be unemployable . There have been many books and presentations at conferences about its core concepts, but they rarely guide developers as to how to adopt it. This article will serve as a guide for learning more about TDD – whether you are an absolute beginner or already have some experience with TDD.
There are a number of ways to get started with TDD:
|Pair up with a more experienced colleague or join an already high performing team that practices TDD.||This opportunity might be hard to come by.|
|Convince your boss to send you on a basic TDD training course for professional developers.||Cost matters – not only are courses expensive, but the time away from work must also be considered and can even be a deal-breaker. Apart from this, the course alternative also gives you the least practical experience in TDD.|
|Just start doing it||It might have a negative effect on productivity and code design. Convincing your manager and all team members to invest in learning TDD might be unrealistic.|
|Learn it by yourself||It might feel overwhelming and it would be hard to know in what order to learn each piece of the TDD puzzle.|
TDD in short
TDD is a software development process that relies on giving quick feedback to the developer through short iterations of the red-green-refactor cycle. The three steps in the cycle consist of:
- Writing a failing test that uses the public API of the intended test target. In this step the developer becomes a client of his own API and can thus see if it is easy to use. This is where TDD is said to drive the design of production code.
- Implement the desired behavior of the test. Because of the next step in the cycle, it is okay to hack away in order to get the test to pass.
- The final step is to use the tests to refactor the code incrementally until there is nothing left to refactor. Then the cycle starts over with a new test to write.
There are two main styles of doing test-driven development – classic TDD and mockist TDD (aka London school). In classic TDD, behavior is mostly verified by comparing actual return values to expected return values. It is often the first choice for algorithmic problems where some input is expected to give some output. Mockist TDD, on the other hand, verifies interactions between collaborators (objects). It is more suitable when your system is event-based, e.g., checking that the save method was invoked with the correct argument when a user clicked the “save” button in a UI.
Each unit test usually consists of three phases:
- Setup – all collaborators (objects) that are needed for the test are created.
- Verify – the return result of the method invocation or interactions between the collaborators are verified.
- Teardown – teardown of resources or state for the test, making the next round of tests totally independent of previous test results.
Setup, verify, and teardown are also known as arrange, act and assert, and annihilate.
Another important concept in unit testing is “test doubles”. A test double is an object that stands in for the real collaborator during a unit test – typically because it is more controllable than relying on the real implementation, which may rely on, e.g., the file system, a network connection, or random behavior. Developers often use a third party library, a mocking framework, to create test doubles. However, such a thing is not required as it is very easy to write your own test doubles. When beginning with TDD, it might even be preferable to create your own test doubles in order to understand their differences. Using a mocking framework is advisable if you are unit testing objects that have dependencies with large public APIs. Here is a short summary of the different kinds of test doubles:
- Stub – an implementation that has hard-coded return values.
- Fake – contains more logic and state than a stub. An example could be a fake database, where we store “entries” in a list.
- Spy – records method invocations and can be interrogated about them afterwards.
- Mock – like a spy, but is self-verifying in terms of expected method invocations.
It makes sense to start doing TDD if you feel comfortable with common design patterns and SOLID principles [4, 5]. It is definitely possible to do test-first development, a related discipline to TDD, without design patterns and SOLID. However, they play a key part in driving the design of production code in test-driven development. The fine difference between the two is that test-driven is more of a design activity whereas test-first just means that the tests are developed first.
It is a logical assumption to make that developing more code takes more time. Developing more code is essentially what you do when using TDD, since abstractions are needed to break dependencies, which in turn makes code easier to test. However, unit tests will come cheaper when developed before production code, since legacy code is likely to have many obscurities that are hard, and thus expensive, to test. Developing the production code with testability in mind makes it significantly easier.
A skilled TDD practitioner will take a longer time to develop the same amount of functionality because more code is produced, but will have an advantage when it comes to code coverage in unit tests, flexibility, and code quality – code that is more resistant to change, easier to change, and less coupled. Furthermore, this means less fear when refactoring faster lead times to act upon change requests.
In order to be a successful TDD practitioner you also need to be a good software designer with firm skills in design principles, design patterns, and refactoring . Without it, it is harder to get started with TDD and odds are that the tests will be too strongly linked to the implementation – so-called fragile tests, which know too much about implementation details and thus break easily when the production code is changed.
Starting out with TDD
When just starting out doing TDD you will take a big productivity hit because of the uncertainty of how to do things. This is expected whenever you try out new things. There is a study that shows that developers inexperienced in TDD can even be disruptive ! I would encourage you to try to learn the basics in a secluded environment where there are no worries such as deadlines. In order to get a feeling for the red-green-refactor cycle, it would be a good idea to start out with a project of an algorithmic nature which has fewer dependencies and focuses on verifying outputs of given inputs rather than interactions between collaborators (the mockist approach). An example of such a project is the roman numerals kata, which is one of many katas that you can find at cyber-dojo . You can read more about this under the “Code Katas” paragraph.
Testing a stateless object where inputs and outputs are the only concerns is easy compared to testing objects which have dependencies – particularly if those dependencies are either static or ad hoc object instantiations (using the “new” keyword and thus not easily replaceable). A useful and common way of isolating dependencies like that is to use dependency injection and use service classes instead. With this approach, dependencies can easily be replaced by stubs, fakes, or mocks, which answer invocations in a controllable way. An example of when such a thing is useful is when we want to test an object that has a dependency to another object that behaves randomly. Then the dependency could be made to behave in an expected way by using a stub.
To the absolute TDD beginner, one of the hardest things is to actually get started with the first test. What is a good first test? In episode 21 of the “Clean Coders” series, Uncle Bob Martin reveals his secret to the first test as “write the test that forces you to write the code you already know you want to write”. So the question you should ask yourself is – if you were not doing TDD, what code would you write to solve your problem? The next step is to write a test which uses your imagined production code. Of course, this test will not compile, which means that the next step is obvious – you make it compile. After you have got it to compile, you will likely want to verify the simplest scenario possible and, when you have done that, you will want to verify the next simplest scenario possible that will not pass the existing tests. Then you continue iterating. It is actually more common to know which test to write than to know the exact production code needed to make a test pass.
Another common obstacle is getting stuck, which means that there is no simple transformation you can perform to the production code to make all tests pass. This usually means that you have cornered yourself implementation-wise. The root cause may be an earlier mistake than the latest test, so it would be wise to back out until you can make progress again. Maybe an early assumption about the problem did not hold?
Another thing to watch out for are anti-patterns such as slow tests, tests that usually pass, tests that need too many dependencies, and tests that try to test too much. Even worse are tests that are fragile – tests that break due to small changes to the production code. Fragile tests are often a consequence of tight coupling with the production code or over-specification of mocks.
Towards learning sustainable TDD
The second half of the article introduces sources I have learned to appreciate while learning TDD.
To learn firsthand from the best developers is a rare thing, but literature gives you the opportunity to do just that. It is also the ideal choice for studying the details up close. Below is a list of books that I have found useful in terms of learning TDD. I have left out language-specific books on purpose, but those interested in doing TDD on embedded platforms should probably check out the pragmatic bookshelf [6, 7].
|9780596008673||Head First Object-Oriented Analysis and Design||Gives visual and fun introductions on object-oriented design and design principles to the beginner who has taken a few programming courses at university.|
|9780596102142||Head First Design Patterns||Gives visual and fun introductions to many design patterns, with questions and answers that many more advanced books skip. This book is relevant if you are new to design patterns or if you would like a fun rehearsal of what you think you already know.|
|In writing||Mocks, Fakes and Stubs||A book which is currently being written, but it is possible buy access to the work-in-progress (leanpub.com). It contains in-depth descriptions of TDD styles and techniques.|
|9780321146533||Test Driven Development: By Example||The original book on TDD, written by Kent Beck, where he illustrates his thought process on TDD and shows how to apply it. If you only have time to read one book on TDD, then this book should be the one you choose.|
|9780201485677||Refactoring: Improving the Design of Existing Code||A classic from Martin Fowler where he introduced the concept of code smells. The book is a reference for refactoring, containing examples and step-by-step procedures of how to perform them.|
|Pending||The Coding Dojo Handbook||An excellent guide when learning TDD. It contains descriptions, examples, and ideas of how to perform and experiment with katas.|
|9780135974445||Agile Software Development, Principles, Practices, and Patterns||This book introduced many of the core OO design principles, which nowadays are known as the SOLID principles. It also demonstrates pair programming, test driven development, design patterns, and how it all comes together in a case study. If you only have time to read one book on software design, then this book should be the one you choose.|
|9780132350884||Clean Code||The first chapters in this book should be mandatory reading for any professional developer, with contents such as function structure, naming, comments, boundaries, etc.|
|9780131177055||Working Effectively with Legacy Code||A book that recognizes that a lot of developers struggle when facing legacy code – code without tests. This book shows techniques of how to get legacy code under test.|
|9780131495050||xUnit Test Patterns: Refactoring Test Code||The bible when it comes to unit testing. Introduces concepts such as test doubles, test phases, test smells, test patterns, etc.|
|9780321503626||Growing Object-Oriented Software, Guided by Tests||This book introduces the London school of TDD (mockist style). The reader can follow along as an example project is developed. It also brings up advanced testing topics such as threading and asynchronous code.|
|9780321213358||Refactoring to Patterns||Author Joshua Kerievsky continues where Martin Fowler left off in “Refactoring”. He makes the connection between refactoring and patterns, and explains how smells can be resolved by refactoring to patterns.|
|9781935182573||Effective Unit Testing: A guide for Java developers||This book is for developers who are already familiar with unit testing and TDD. With a focus on maintainability and test design, it takes up concrete examples of bad tests and shows how to make the better. The book uses java, but the techniques used are applicable to other languages as well.|
Todays connected world has brought further possibilities for developers to learn software practices in the form of online courses. Both Pluralsight and Udemy have great app support, while Industrial Logic’s solution is completely web-based, except for the exercises.
|Online learning sites|
|pluralsight.com||$29, $49 /month||Pluralsight is the biggest online learning resource for developers. It is subscription-based and costs either $29 or $49 per month, depending on account type. The cheaper account gives access to online video lectures, while the more expensive option also features offline videos, quizzes and, in some cases, exercises. Pluralsight has a huge course library where courses relevant to TDD can be found under the “software practices” category. New users have the opportunity to get a 10-day free trial.|
|udemy.com||FREE, paid||Udemy generally has an even bigger library than Pluralsight, but does not specifically target developers. There are lots of courses on programming and a few focus on TDD, refactoring, and design patterns. Udemy offers both free and paid courses – prices are up to the instructor. Another thing worth noting is that Udemy regularly has coupon sales, which often means you can get up to 80% discount.|
|industriallogic.com||$140-$250 per course, discount on box sets||Industrial Logic has several e-learning courses related to TDD, refactoring, legacy code, code smells, faking and mocking, design patterns, etc. They are a bit costly but contain more practical exercises than both Udemy and Pluralsight. Each course has reading material, videos, and quizzes, and most have practical coding exercises.|
The video series differ to the courses in that they have no exercises or associated reading materials. They also lack the app support.
|cleancoders.com||$12, $18, ~1hour episodes||Uncle Bob’s series that expands on the contents of his books – Clean Code, The Clean Coder, and Agile Software Development: Principles, Patterns, and Practices. As of now it includes “Clean Code Fundamentals”, “Solid Principles”, “Component Design”, and “Advanced TDD”. The latter is probably among the most in-depth TDD video that it is possible to find on the web.|
|pragprog.com||$30, ~2 hours||Kent Beck has a smaller series where he shows TDD in action.|
|jamesshore.com||FREE||With more than 200 episodes in his video series “Let’s Play TDD”, James Shore has the longest running TDD-focused video series on the web. Each episode is about 15 minutes long and showcases TDD in a real software project.|
There are no magazines about TDD in particular, but it might come in handy to know that there are a couple that occasionally write about related topics.
|NFJS, the Magazine – nofluffjuststuff.com/home/magazine_subscribe||$50/10 issues||Features articles on software development. All authors are speakers on the No Fluff Just Stuff Tour.|
|Pragpub – pragprog.com/magazines||Old issues for FREE, $2/new issue||Gurus of software development are regularly featured as authors. They often have article series spanning over multiple issues. Pragpub has clients on Android and iOS.|
|Agile Record – agilerecord.com||FREE||Not released as frequently as the others, but is totally free. The target audience is managers, testers, and developers. Each issue has its own technology focus.|
The word “kata” was first mentioned in a programming context by Dave Thomas and refers to a form of karate training where one move is repeated over and over until perfection is met. Code katas are smaller programming exercises where programmers can practice everyday skills such as TDD, refactoring, and IDE shortcuts. Some exercises might be logically hard to solve, but normally lack the complexity that corporate environments often have with lots of dependencies. Learning how to handle dependencies in a unit-testing context is something that will come with experience when working in a corporate environment. A good place to start experimenting with katas is http://www.cyber-dojo.com, where it is possible to develop in the browser. However, without an IDE you will miss out on honing some of your everyday skills like keyboard shortcuts.
While Code katas are perfect as muscle memory exercises and for learning new techniques, they lack the realism of a real project. Pet projects have a bigger scope, which adds to the realism. In practice this means better opportunities for learning frameworks and a more realistic software architecture. In , author Sandro Mancuso suggests that pet projects should be about things that you are passionate about. This emotional attachment manifests itself as a stream of feature requests and excitement.
Open source is an even more realistic way of practicing. In fact, many companies take inspiration from open source. An interesting aspect of open source is the opportunity to learn from code committed by peers. Open source gives you the opportunity to learn from code committed by your peers. As you commit code you will also keep building on your own brand. Sandro Mancuso suggests that you start with small contributions in .
TDD involves many other software practices like refactoring, design principles, and unit testing. With it comes quite a steep learning curve, particularly in a corporate environment where legacy code and hard dependencies might pose problems. It is hard justifying head-first TDD in a corporate environment because of the initially lowered productivity and the risk of creating fragile tests – the kind of tests that break when they should not, or for the wrong reasons. Instead, you are recommended to start small in a secluded environment and learn the basics without external pressure. With this basic TDD experience, developers will feel much more confident when applying it in a corporate environment and tackling complex problems like dependencies that rely on external resources.
In todays connected world, there are lots of resources for developers to learn TDD and its related software practices. Some are free, some cost money, and they are available in all levels of difficulty. All providers have material targeted at different skill levels. However, their price plans differ. For developers seeking an occasional competence boost or inspiration, I would recommend magazines or one-time payment videos or courses. For those seeking constant competence training, then Pluralsight is a good choice because of its broad course library with good depth at a predetermined cost. Developers seeking practical experience in TDD should have a look at Industrial Logic’s e-learning courses – if they are too pricey, then buy a copy of “The Coding Dojo Handbook” and read it while practicing the katas available at cyber-dojo .For the advanced TDD practitioner, Uncle Bob Martin’s “Advanced TDD” series will likely be of interest.
- Buchan, J.; Ling Li; MacDonell, S.G., „Causal Factors, Benefits and Challenges of Test-Driven Development: Practitioner Perceptions,“ Software Engineering Conference (APSEC), 2011
- Martin, Robert Cecil. Agile Software Development: Principles, Patterns, and Practices. Prentice Hall PTR, 2003.
- Modern C++ Programming with Test-Driven Development: Code Better, Sleep Better. Pragmatic Bookshelf, 2013.
- Test-Driven Development for Embedded C. Pragmatic Bookshelf, 2011.
- Software Craftsmanship Professionalism Pragmatism Pride, 2014. https://leanpub.com/socra