A 3-day course for developers, providing hands-on experience with the techniques of Test-Driven Development (TDD). This course is designed for experienced developers who are comfortable with their programming language and the basics of object-oriented design. Attendees learn the techniques of test-first, refactoring, mock objects, and others. They learn how these techniques provide and maintain a very low defect-count, plus why TDD helps developers work fearlessly, swiftly, and comfortably on new features and bug-fixes. They will also learn how to work on legacy code: Building test-coverage for critical areas, and protecting areas of the legacy system that do not yet require any alteration.
This set of practices for developers is at the heart of low-defect Agile software development. These techniques allow incremental development and Emergent Design to flourish, without degrading quality. This 3-day course also contains a significant section on the not-so-pleasant task of adding unit tests to legacy code. The course is currently offered in Java or C#..
Intermediate to experienced.
Competence with either the Java or C# programming languages. A familiarity with basic object-oriented principles of design. Basic familiarity with an Agile process such as Scrum or XP.
Computers with proper development environments are required for this course, and will be provided by the customer (1 machine for every 2 students), unless other arrangements have been made.
A computer projector and screen will be necessary, and will be provided by the customer, unless other arrangements have been made.
1.The history and value of TDD.
2. The five practices that comprise TDD.
3. The Test-First mindset, and using it as Just-In-Time problem analysis.
4. Refactoring as Just-In-Time design.
5. Emergent Design and simple design.
6. Using Mock Objects to decouple difficult dependencies.
7. Adding tests to legacy code.
Introductions and logistics
Getting settled in: Choosing lab partners, installing a test framework, setting up the IDE, and getting the very first (canned) test to pass.
High-level Overview, Basics, and Getting Started
Basic syntax for the xUnit family of unit-testing frameworks.
Group TDD Exercise: Attendees are guided through a lab demonstrating:
- Intention-revealing test names.
- Fake It.
- Reducing duplication in tests.
- Obvious Implementation.
- Additional test-framework syntax.
- Testing for thrown exception.
- Thinking Test-First.
We review The Basic Steps to see where people usually stumble at first.
Parsing “Unit Test.” We discuss what is a “unit” of software, and what makes something a “test.”
The Big Picture Exercise: Groups learn and discuss the five core practices of TDD: Test-first, refactoring, a to-do list, mock objects, and automation.
Discipline: A user-friendly definition.
Exercise: One of two options (chosen by instructor and attendees based on attendees’ level of experience):
1. Refactoring Round-Table Exercise: Groups select refactorings to learn, then create a short presentation about the refactorings. Time is provided for whole-group discussion.
2. Code-Smell/Refactoring Etudes.
Tested-Trek Exercise: Attendees refactor a messy blob of code into an acceptable design. Learning outcomes include:
- Developing a sense of comfort and courage from a comprehensive suite of tests.
- Knowing when to run the tests.
- Knowing what to do if a test fails during refactoring.
- Refining a sense for Code Smells.
Exercise retrospective: A Gallery-Walk of resulting software designs as UML sketches or as code. (Attendees can opt out of displaying their designs.)
Refactoring and Design
The Developer’s “Oath of Athens.”
A brief review of Kent Beck’s “Simple Design” rules, Emergent Design, and possibly other descriptions of good software design (including Discoveries of the Gang of Four, Foundational Principles, McConnell’s Code Qualities).
Refactoring to OCP
Maps of Mars: Demonstrating the Open-Closed Principle (OCP), Refactoring, and Emergent Design.
Exercise: Adding features to Tested-Trek using the “Refactoring to OCP” technique.
Just-in-time problem analysis, and how to think of a unit-test as an experiment.
The computer-science perspective: How to think of a unit-test as a simple state diagram.
Password Checker Exercise: An exercise that quickly pushes developers into object-oriented thinking. Learning outcomes include:
- How to think about the problem-space and look for real boundaries, and real variations.
- How to write behavioral unit tests.
- The relationships between tests and design.
- How tests alter design.
- How tests allow design to change.
- How writing tests provides feedback when a design needs to change.
The Broken Set Exercise: This is another problem-analysis challenge revealing the need to drive implementation from the interface, and to consider the essence of an object’s responsibilities, rather than the particular engineering challenges, when designing an object.
Mock Objects – Part I
(Time permitting) The Mock Historian Exercise: This is a non-technical introduction to Mock Objects. Mocks are covered in detail on Day 2. This activity is a nice way to end the day, and gets people thinking about the possibilities.
Mock Objects – Part II
Painful dependencies: What to do when a dependency is in the system is slow, expensive, difficult to construct, or non-deterministic.
The two simple approaches to building mocks: Manual, and using a framework. Attendees learn the costs and benefits of both methods in terms of capability and readability.
Other potential discussion topics:
- Esoteric mocking: Self-shunt—why it's valuable, why it's vague, and how to decide whether or not to use it.
- Dangerous ways to introduce the mock to the object under test: Conditional compiles, factory flags, configuration files. Why they're dangerous.
- When you shouldn't mock it: Examples of areas where you cannot mock further, and what to do instead. E.g., stored procedures.
The LunEx Exercise: Attendees solve the “impossible” testing problem, twice.
Definitions of “Legacy”: From attendees, from James Shore, and from Michael Feathers.
What code to deal with today.
The testing-refactoring chicken-egg problem, and how to live with compromise. Examples:.
- Use the tools available: Extract Method to separate behavior from initialization.
- Refactoring to a mindless Proxy.
Part I: Ask the three questions: What needs testing? What's stopping us? What can we do about that?
Part II: Add at least one "pinning" tests to Mess-Trek.
Optional discussion: A way to introduce a mock into legacy code: The Factory Method pattern and the Testable Subclass. Why it would ever be necessary. Why it’s even more compromising.
Putting It All Together
We briefly review the five practices comprising TDD, and the steps.
The Battleship Game Exercise (or alternatives): We select an immersive lab project and build it using all TDD techniques.
Immersion Lab Retrospective.
What we have learned, and what we can implement immediately.
We offer attendees the opportunity to receive tagging (certification) via Entaggle (www.Entaggle.com).
Attendees complete evaluation forms.
Attendees have opportunity to share their solutions.
Other topics, time permitting:
TDD and Agile
The Agilists' Dilemma: When iterative work generates debt.
Forms of software debt (Design Debt , Quality Debt, Testing Debt) and what to do about them.
ATDD (Acceptance-Test Driven Development) and TDD (Developer Test-Driven Development): The differences, and why they are both important practices.
Building the right software through Acceptance-Test Driven Development.
The tools: FIT (and similar) and Cucumber (and similar) tools.
Acceptance Tests as examples.
The Wizard behind the Curtain: How textual tests in plain English can talk to the system-under-test.