Agile TDD for Embedded Systems and Legacy Code: Course PLUS Your-Legacy-Code Clinic
Revision as of 16:46, 4 June 2011 by Clarman
This is a combination of a 2-3 day course with structured learning exercises, FOLLOWED BY a 2-3 day "clinic" or workshop applying the skills to your existing legacy code. (For a total of 4-5 days; the exact timing is content dependent on the knowledge of the developers, the state of the legacy code, and the legacy problems to tackle).
This information-packed and hands-on course shows developers and technical leaders how to apply test-driven development (TDD) and refactoring in the context of embedded systems and your related legacy code — almost always, C and/or C++, perhaps combined with some assembler.
TDD is powerful and practical. It’s the practice of always writing test code before the code to be tested, in small micro-test-and-develop cycles. In addition to the obvious benefits that (1) tests actually get written and executed for most code with thorough test coverage and (2) the practice and mindset of "building quality in" by test-FIRST rather than test-LAST (which tries to "inspect old defects out"), a more subtle but important benefit is that (3) when we start by thinking very concretely—with code—in the role of a calling client to the new code before it is written, it (4) clarifies our design, (5) tends to create better designs with lower coupling, higher cohesion, and flexible dependency injection, (6) and becomes a more fun and creative way to combine writing tests with code. Hence, TDD is far more than "just testing" — it is a kind of creative micro-design step that drives better design.
In this course you will learn how to think in and apply TDD, and establish it as a consistent behavior for your development team, in the context of embedded-systems design and C++ and/or C. You’ll learn and work with a popular free open-source TDD frameworks useful for embedded systems, such as Google Test, CppUTest, Unity, Google Mock, and C Mock.
TDD quickly leads developers to see the need for and value of reducing coupling in their code, and for techniques to break dependencies so that tests can be run quickly in isolation. Thus, a critical adjunct skill in TDD is learning how to create and inject alternate “test doubles” (fakes, stubs, ...). In this course you will learn how to create stubs, fakes, mocks, object factories/mothers, how to break dependencies, and how to apply dependency injection methods.
Learning how to break dependencies for testing in isolation is especially important in the context of legacy code; in this course you will to work with your legacy code to break dependencies, “bring it under test”, introduce flexible configuration in your code, and apply TDD.
Refactoring is a disciplined design skill to improve the structure of code without changing its external behavior. And refactoring is part of the TDD cycle. Thus, in this course you will learn the various “code smells” and the refactorings to clean them up. Refactoring is aided on automated refactoring tools built into popular IDEs or editors, such as the Eclipse CDT, SlickEdit, or emacs; thus in this course you will learn to apply an automated refactoring tool, in addition to manual refactoring.
STEP ONE - Course (2-3 days): You will learn to apply all these skills in the context of an exercise to develop a device driver or other low-level embedded-systems components in C or C++ while applying TDD and refactoring.
STEP TWO - Clinic/Workshop (2-3 days): Now, onwards to the messy reality of your existing legacy code. The teacher/coach will start by demonstrating how to work with your embedded-systems legacy code, to break dependencies and apply TDD and refactoring, in a "coding dojo" style workshop. This is followed by small groups of the developers (for example, 2 people together), working in parallel on different sections of their legacy code to break dependencies, bring it "under test", and start to apply TDD and refactoring to it. During this phase, the coach will rotate across all the groups, giving guidance and feedback. There will also be some periods of "show and tell", looking at the existing solutions the smaller groups are creating with your legacy code.
Methods of Education
Discussion, presentation, Q&A, workshop exercises
Developers, architects, test engineers, technical leaders.
Intermediate: This course introduces concepts and techniques that the attendee will apply during the workshop.
skill in programming; knowledge of your legacy code
Upon completion of this course, students should be able to:
- apply TDD
- break dependencies and create “test doubles” (fakes, mocks, stubs, ...)
- inject dependencies with flexible techniques
- separate test set up code into object factories or “object mothers”
- identify code smells
- apply refactorings
- use an xUnit framework such as Junit
- bring legacy code “under test”
- define acceptance TDD and the FIT framework
- Test-Driven Development
- Method and motivation
- Writing tests first
- The TDD lifecycle
- Testing in an iterative and agile method
- Categories of TDD: unit, acceptance
- TDD tools and frameworks
- Testing in different architectural layers
- TDD Tools
- Unit TDD
- Practice with XUnit
- Code smells
- Refactorings, including Extract Method, Introduce Explaining Variable, and dozens more
- Test Doubles: Fakes, Stub and Mock Objects
- Integration vs. unit testing
- Setting up the test environment
- Mock generation tools
- Object factories
- Object Mother pattern
- Dependency injection
- Dependency injection with Spring
- Continuous Integration and TDD
- Information radiators for CI
- Why do people delay integration?
- Acceptance TDD with FIT
- TDD and Legacy Code
- Characterization tests
- The Legacy TDD life cycle
Special C, C++ and Embedded Topics (introduced as needed)
- implementing abstract data types (ADTs) in C -- "objects in C"
- single-instance and multi-instance ADTs
- agile modeling with ADTs, and mapping agile models to C
- weak versus strong ADTs
- unit TDD for C/C++
- test doubles for C/C++ with polymorphic- ,link- , preprocessor- , meta-programming- (functors and function pointers), and configuration seams
- test-doubles for lower-level components: device drivers, etc.
- create device drivers and other low-level components with TDD in C++ and/or C
- clean code and refactoring in C/C++
- dual targeting and TDD
- mock objects in C and C++
- combining C, C++, assembler, and inline assembler