Three Law of TDD - to strictly follow, or not?
"Kaleb Pederson [email protected] [testdrivendevelopment]" <[email protected]> Tue, 26 Aug 2014 09:48:39 -0600
| Newsgroups | gmane.comp.programming.test-driven-development |
|---|---|
| Message-ID | <CAF4m_uXYrD6dhEZtJ8LehWd7NX9ip7tgQW9RxsMmDa2TdLeYfA@mail.gmail.com> |
Over the last three years I've been doing training on and advocating TDD
where I work and a number of people have been striving to learn and use TDD
in their daily development :).
--- An experience with coworker 1 ---
One of these coworkers recently published a 6-part series on TDD that
starts off assuming no prior experience with TDD (
https://www.simple-talk.com/dotnet/.net-framework/a-tdd-journey-1-trials-and-tribulations/
).
In part two, having recently introduced the three laws of TDD, he wrote the
following (
https://www.simple-talk.com/dotnet/.net-framework/a-tdd-journey-2--naming-tests-mocking-frameworks-dependency-injection/
):
[Test]
public void Execute_delegates_to_IWidgetLoader_to_load_widget_details()
{
var activator = new WidgetActivator();
}
At this point the test fails because WidgetActivator doesn't exist. He then
proceeds to introduce a dependency and a constructor parameter before
making the test pass:
var mockWidgetLoader = new Mock<IWidgetLoader>();
var activator = new WidgetActivator(mockWidgetLoader.Object);
When a commenter pointed out that he was violating the three laws of TDD of
he commented:
"When adding a new test, you do not stop as soon as it turns red; rather
you stop when the test is completely written. Then you labor to make that
test turn green by modfiying [sic] the class-under-test. Think about it
this way: if you write a partial test then make it go green, you now have a
passing test *that is wrong*. A test should be green if and only if it is
valid."
I disagree with the comment because it allows multiple unnecessary lines of
code to be written and doesn't provide a progression that guarantees that
all production code is effectively "tested"/covered. It also makes it
harder to get feedback from the test about the design of the system. Sure,
the developer might tweak the test code to improve the API but at that
point the API design isn't supported by a working implementation so it's
not necessarily going to reveal design defects as quickly.
Another person pointed out that "an incomplete but green test is at best
misleading and at worst wrong: if a test is green it should be a valid
test, and a partial or incomplete test is not valid."
--- An experience with coworker 2 ---
Coworker 2 recently came to me with some questions. He described his
process wherein he would:
* Create an empty test
* Add a variable assignment: var instance = new MyClass();
* Introduce the class
* Add dependency1 to that class: var instance = new MyClass(dep1);
* Add that dependency as a constructor parameter
* Add dependency2 to that class: var instance = new MyClass(dep1, dep2);
* Call the method to be under test: var result = instance.Execute();
* Define the method under test
* Add a parameter to the callsite of the method under test: var result =
instance.Execute(param1);
* Add the new parameter to the implementation of the method under test
* Add another parameter to the callsite of the method under test: var
result = instance.Execute(p1, p2);
* ...
The story, in short, is that no matter what he was doing extremely small
steps -- steps that felt unnaturally small to me. Ultimately he had a
number of problems which seemed to be because he wasn't letting the tests
guide the implementation but he felt the implementation went really slowly
because after writing a fair amount of code and he'd have to come back and
modify many of his tests when a new parameter to a method needed to be
added.
I introduced him to ATDD/double-loop TDD in hopes that it'd help him work
in vertical slices, let the system evolve, and help him discover the
required parameters/dependencies early in the process.
--- Questions ---
Both of the above experiences and questions go back to understanding the
three laws of TDD and step size. As I'm entirely self taught, I'd like to
know your thoughts.
Do you strictly follow the three laws of TDD as literally written?
What step size do you typically have when going back and forth between
tests and code?
How do you decide the appropriate step size?
When writing a test, do you define a method or constructor with all its
expected parameters?
Do you consider, "an incomplete but green test is at best misleading and at
worst wrong"?
Thank you for the feedback.
--Kaleb