![](/user_photo/_userpic.png)
- •Table of Contents
- •About the Author
- •About the Technical Reviewer
- •Acknowledgments
- •Software Entropy
- •Clean Code
- •C++11: The Beginning of a New Era
- •Who This Book Is For
- •Conventions Used in This Book
- •Sidebars
- •Notes, Tips, and Warnings
- •Code Samples
- •Coding Style
- •C++ Core Guidelines
- •Companion Website and Source Code Repository
- •UML Diagrams
- •The Need for Testing
- •Unit Tests
- •What About QA?
- •Rules for Good Unit Tests
- •Test Code Quality
- •Unit Test Naming
- •Unit Test Independence
- •One Assertion per Test
- •Independent Initialization of Unit Test Environments
- •Exclude Getters and Setters
- •Exclude Third-Party Code
- •Exclude External Systems
- •What Do We Do with the Database?
- •Don’t Mix Test Code with Production Code
- •Tests Must Run Fast
- •How Do You Find a Test’s Input Data?
- •Equivalence Partitioning
- •Boundary Value Analysis
- •Test Doubles (Fake Objects)
- •What Is a Principle?
- •KISS
- •YAGNI
- •It’s About Knowledge!
- •Building Abstractions Is Sometimes Hard
- •Information Hiding
- •Strong Cohesion
- •Loose Coupling
- •Be Careful with Optimizations
- •Principle of Least Astonishment (PLA)
- •The Boy Scout Rule
- •Collective Code Ownership
- •Good Names
- •Names Should Be Self-Explanatory
- •Use Names from the Domain
- •Choose Names at an Appropriate Level of Abstraction
- •Avoid Redundancy When Choosing a Name
- •Avoid Cryptic Abbreviations
- •Avoid Hungarian Notation and Prefixes
- •Avoid Using the Same Name for Different Purposes
- •Comments
- •Let the Code Tell the Story
- •Do Not Comment Obvious Things
- •Don’t Disable Code with Comments
- •Don’t Write Block Comments
- •Don’t Use Comments to Substitute Version Control
- •The Rare Cases Where Comments Are Useful
- •Documentation Generation from Source Code
- •Functions
- •One Thing, No More!
- •Let Them Be Small
- •“But the Call Time Overhead!”
- •Function Naming
- •Use Intention-Revealing Names
- •Parameters and Return Values
- •Avoid Flag Parameters
- •Avoid Output Parameters
- •Don’t Pass or Return 0 (NULL, nullptr)
- •Strategies for Avoiding Regular Pointers
- •Choose simple object construction on the stack instead of on the heap
- •In a function’s argument list, use (const) references instead of pointers
- •If it is inevitable to deal with a pointer to a resource, use a smart one
- •If an API returns a raw pointer...
- •The Power of const Correctness
- •About Old C-Style in C++ Projects
- •Choose C++ Strings and Streams over Old C-Style char*
- •Use C++ Casts Instead of Old C-Style Casts
- •Avoid Macros
- •Managing Resources
- •Resource Acquisition Is Initialization (RAII)
- •Smart Pointers
- •Unique Ownership with std::unique_ptr<T>
- •Shared Ownership with std::shared_ptr<T>
- •No Ownership, but Secure Access with std::weak_ptr<T>
- •Atomic Smart Pointers
- •Avoid Explicit New and Delete
- •Managing Proprietary Resources
- •We Like to Move It
- •What Are Move Semantics?
- •The Matter with Those lvalues and rvalues
- •rvalue References
- •Don’t Enforce Move Everywhere
- •The Rule of Zero
- •The Compiler Is Your Colleague
- •Automatic Type Deduction
- •Computations During Compile Time
- •Variable Templates
- •Don’t Allow Undefined Behavior
- •Type-Rich Programming
- •Know Your Libraries
- •Take Advantage of <algorithm>
- •Easier Parallelization of Algorithms Since C++17
- •Sorting and Output of a Container
- •More Convenience with Ranges
- •Non-Owning Ranges with Views
- •Comparing Two Sequences
- •Take Advantage of Boost
- •More Libraries That You Should Know About
- •Proper Exception and Error Handling
- •Prevention Is Better Than Aftercare
- •No Exception Safety
- •Basic Exception Safety
- •Strong Exception Safety
- •The No-Throw Guarantee
- •An Exception Is an Exception, Literally!
- •If You Can’t Recover, Get Out Quickly
- •Define User-Specific Exception Types
- •Throw by Value, Catch by const Reference
- •Pay Attention to the Correct Order of Catch Clauses
- •Interface Design
- •Attributes
- •noreturn (since C++11)
- •deprecated (since C++14)
- •nodiscard (since C++17)
- •maybe_unused (since C++17)
- •Concepts: Requirements for Template Arguments
- •The Basics of Modularization
- •Criteria for Finding Modules
- •Focus on the Domain of Your Software
- •Abstraction
- •Choose a Hierarchical Decomposition
- •Single Responsibility Principle (SRP)
- •Single Level of Abstraction (SLA)
- •The Whole Enchilada
- •Object-Orientation
- •Object-Oriented Thinking
- •Principles for Good Class Design
- •Keep Classes Small
- •Open-Closed Principle (OCP)
- •A Short Comparison of Type Erasure Techniques
- •Liskov Substitution Principle (LSP)
- •The Square-Rectangle Dilemma
- •Favor Composition over Inheritance
- •Interface Segregation Principle (ISP)
- •Acyclic Dependency Principle
- •Dependency Inversion Principle (DIP)
- •Don’t Talk to Strangers (The Law of Demeter)
- •Avoid Anemic Classes
- •Tell, Don’t Ask!
- •Avoid Static Class Members
- •Modules
- •The Drawbacks of #include
- •Three Options for Using Modules
- •Include Translation
- •Header Importation
- •Module Importation
- •Separating Interface and Implementation
- •The Impact of Modules
- •What Is Functional Programming?
- •What Is a Function?
- •Pure vs Impure Functions
- •Functional Programming in Modern C++
- •Functional Programming with C++ Templates
- •Function-Like Objects (Functors)
- •Generator
- •Unary Function
- •Predicate
- •Binary Functors
- •Binders and Function Wrappers
- •Lambda Expressions
- •Generic Lambda Expressions (C++14)
- •Lambda Templates (C++20)
- •Higher-Order Functions
- •Map, Filter, and Reduce
- •Filter
- •Reduce (Fold)
- •Fold Expressions in C++17
- •Pipelining with Range Adaptors (C++20)
- •Clean Code in Functional Programming
- •The Drawbacks of Plain Old Unit Testing (POUT)
- •Test-Driven Development as a Game Changer
- •The Workflow of TDD
- •TDD by Example: The Roman Numerals Code Kata
- •Preparations
- •The First Test
- •The Second Test
- •The Third Test and the Tidying Afterward
- •More Sophisticated Tests with a Custom Assertion
- •It’s Time to Clean Up Again
- •Approaching the Finish Line
- •Done!
- •The Advantages of TDD
- •When We Should Not Use TDD
- •TDD Is Not a Replacement for Code Reviews
- •Design Principles vs Design Patterns
- •Some Patterns and When to Use Them
- •Dependency Injection (DI)
- •The Singleton Anti-Pattern
- •Dependency Injection to the Rescue
- •Adapter
- •Strategy
- •Command
- •Command Processor
- •Composite
- •Observer
- •Factories
- •Simple Factory
- •Facade
- •The Money Class
- •Special Case Object (Null Object)
- •What Is an Idiom?
- •Some Useful C++ Idioms
- •The Power of Immutability
- •Substitution Failure Is Not an Error (SFINAE)
- •The Copy-and-Swap Idiom
- •Pointer to Implementation (PIMPL)
- •Structural Modeling
- •Component
- •Interface
- •Association
- •Generalization
- •Dependency
- •Template and Template Binding
- •Behavioral Modeling
- •Activity Diagram
- •Action
- •Control Flow Edge
- •Other Activity Nodes
- •Sequence Diagram
- •Lifeline
- •Message
- •State Diagram
- •State
- •Transitions
- •External Transitions
- •Internal Transitions
- •Trigger
- •Stereotypes
- •Bibliography
- •Index
Chapter 8 Test-Driven Development
•\ RED: We write one failing unit test.
•\ GREEN: We write just enough production code—and not one line more!—so that the new test and all previously written tests will pass.
•\ REFACTOR: Code duplication and other code smells are eliminated, both from the production code as well as from the unit tests.
The terms RED and GREEN refer to typical unit test framework integrations that are available for a variety of IDEs (Integrated Development Environments), where tests that passed are displayed in green and tests that failed are shown in red.
Enough of theory, I will now explain the complete development of a piece of software using TDD and a small example.
TDD by Example: The Roman Numerals Code Kata
The basic idea for what is nowadays called a code kata was first described by Dave Thomas, one of the two authors of the remarkable book, The Pragmatic Programmer [Hunt99]. Dave was of the opinion that developers should practice on small, not job- related, code bases repeatedly so that they can master their profession like a musician. He said that developers should constantly learn and improve themselves, and for that purpose, they need practice sessions to apply the theory over and over again, using feedback to get better every time.
A code kata is a small exercise in programming, which serves exactly this purpose. The term kata is inherited from the martial arts. In far-eastern combatant sports, they use katas to practice their basic moves over and over again. The goal is to bring the course of motion to perfection.
This kind of practice was devolved to software development. To improve their programming skills, developers should practice their craft with the help of small exercises. Katas became an important facet of the Software Craftsmanship movement. They can address different abilities a developer should have, for example, knowing the keyboard shortcuts of the IDE, learning a new programming language, focusing on certain design principles, or practicing TDD. On the Internet, several catalogues with suitable katas for different purposes exist, for example, the collection by Dave Thomas on http://codekata.com.
For our first steps with TDD, we use a code kata with an algorithmic emphasis: the well-known Roman numerals code kata.
342
![](/html/75672/2303/html_3iAzqyDIia.Sav3/htmlconvd-3gbB7Q353x1.jpg)
Chapter 8 Test-Driven Development
TDD KATA: CONVERT ARABIC NUMBERS TO ROMAN NUMERALS
The Romans wrote numbers using letters. For instance, they wrote “V” for the Arabic number 5.
Your task is to develop a piece of code using the test-driven development (TDD) approach that translates the Arabic numbers between 1 and 3,999 into their respective Roman representations.
Numbers in the Roman system are represented by combinations of letters from the Latin alphabet. Roman numerals, as used today, are based on seven characters:
1 I
5 V
10 X
50 L
100 C
500 D
1,000 M
Numbers are formed by combining characters together and adding the values. For instance, the Arabic number 12 is represented by “XII” (10 + 1 + 1). And the number 2017 is “MMXVII” in its Roman equivalent.
Exceptions are 4, 9, 40, 90, 400, and 900. To avoid that four equal characters must be repeated in succession, the number 4, for instance, is not represented by “IIII”, but “IV”. This is known as subtractive notation, that is, the number that is represented by the preceding character I is subtracted from V (5 - 1 = 4). Another example is “CM,” which is 900 (1,000 - 100).
By the way, the Romans had no equivalent for 0 (zero); furthermore, they didn’t know negative numbers.
Preparations
Before we can write our first test, we need to make some preparations and set up the test environment.
343
Chapter 8 Test-Driven Development
As the unit test framework for this kata, I use Google Test (https://github.com/ google/googletest), a platform-independent C++ unit test framework released under the New BSD License. Of course, any other C++ unit testing framework can be used for this kata as well.
It is also strongly recommended to use a version control system. Apart from a few exceptions, we will perform a commit to the version control system after each pass- through of the TDD cycle. This has the great advantage that we can walk back and regress possibly wrong decisions.
Furthermore, we have to think about how the source code files will be organized. My suggestion for this kata is initially to start with just one file, the file that will take up all future unit tests: ArabicToRomanNumeralsConverterTestCase.cpp. Since TDD guides us incrementally through the formation process of a software unit, it is possible to decide later if additional files are required.
For a fundamental function check, we write a main function that initializes Google Test and runs all tests, and we write one simple unit test (named
PreparationsCompleted) that always fails intentionally, as shown in the code example in Listing 8-1.
Listing 8-1. The Initial Content of ArabicToRomanNumeralsConverterTestCase.cpp
#include <gtest/gtest.h>
int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();
}
TEST(ArabicToRomanNumeralsConverterTestCase, PreparationsCompleted) { GTEST_FAIL();
}
After compiling and linking, we execute the resulting binary file to run the test. The output of our small program on standard output (stdout) should be as shown in Listing 8-2.
344
![](/html/75672/2303/html_3iAzqyDIia.Sav3/htmlconvd-3gbB7Q355x1.jpg)
Chapter 8 Test-Driven Development
Listing 8-2. The Output of the Test Run
[==========] |
Running 1 test from 1 test case. |
|||
[----------] |
Global test environment set-up. |
|||
[----------] |
1 |
test from ArabicToRomanNumeralsConverterTestCase |
||
[ RUN |
] |
ArabicToRomanNumeralsConverterTestCase.PreparationsCompleted |
||
../ ArabicToRomanNumeralsConverterTestCase.cpp:9: Failure |
||||
Failed |
|
|
|
|
[ |
FAILED |
] |
ArabicToRomanNumeralsConverterTestCase.PreparationsCompleted |
|
(0 |
ms) |
|
|
|
[----------] |
1 |
test from ArabicToRomanNumeralsConverterTestCase (2 ms total) |
||
[----------] |
Global test environment tear-down |
|||
[==========] |
1 |
test from 1 test case ran. (16 ms total) |
||
[ |
PASSED |
] |
0 |
tests. |
[ |
FAILED |
] |
1 |
test, listed below: |
[ |
FAILED |
] |
ArabicToRomanNumeralsConverterTestCase.PreparationsCompleted |
|
1 |
FAILED TEST |
|
Note Depending on the unit test framework and its version used, the output may be different than what is presented in this example.
As expected, the test fails. The output on stdout is pretty helpful to imagine what went wrong. It specifies the name of the failed tests, the filename, the line number, and the reason that the test failed. In this case, it is a failure that was enforced by a special Google Test macro.
If we now exchange the GTEST_FAIL() macro with the GTEST_SUCCEED() macro inside the test, after a recompilation the test should pass, as shown in Listing 8-3.
Listing 8-3. The Output of the Successful Test Run
[==========] |
Running 1 test from 1 test case. |
|||
[---------- |
] |
Global |
test |
environment set-up. |
[---------- |
] |
1 test |
from |
ArabicToRomanNumeralsConverterTestCase |
[ RUN |
] |
ArabicToRomanNumeralsConverterTestCase.PreparationsCompleted |
||
[ |
OK ] |
ArabicToRomanNumeralsConverterTestCase.PreparationsCompleted (0 ms) |
345