Click here to monitor SSC
  • Av rating:
  • Total votes: 27
  • Total comments: 8
Michael Sorens

A TDD Journey: 1-Trials and Tribulations

15 July 2014

Test-Driven Development (TDD) has a misleading name, because the objective is to design and specify that the system you are developing behaves in the ways that the customer expects, and to prove that it does so for the lifetime of the system. It isn't an intuitive way of coding but by automating the specifications of a system, we end up with tests and documentation as a by-product. Michael Sorens starts an introduction to TDD that is more of a journey in six parts:

Part 1: Trials and Tribulations of TDD

 

Part 2: Naming Tests; Mocking Frameworks; Dependency Injection

 

Part 3: Mocks vs. Stubs; Test Frameworks; Assertions; ReSharper Accelerators

 

Part 4: Tests as Documentation; False Positive Results; Component Isolation

 

Part 5: Tests vs. Code; Refactor Friendliness; Test Parameterization

 

Part 6: Mini-Factory Pattern; Don’t Care Terms

Introduction

Ask someone experienced in Test Driven Development (TDD)—someone who has used it successfully—to show you how to practice it, and he/she will show you the correct way to do it, and you will be off and running. But… if you then ask someone else experienced in TDD you will learn a different, yet also correct, way to do it. In fact, everyone will develop their own unique style for practicing TDD. So what you are about to read is how I approach TDD. It is not “the” way to practice TDD but “a” way. Of course I am biased when I say that along the spectrum of good and bad TDD styles, it is a pretty good way to do TDD. But I will back that up by saying that (a) it is not really “mine” in isolation but rather it is my personal slant on a style mutually developed by the project team I work with, and (b) it evolved and was used on a reasonably complex, enterprise application over the course of more than a year and it worked (nay, continues to work) very well.

A Never-Before Attempted Feat of Derring-Do

Your average article aspiring to teach TDD provides a lot of theory then suggests you find some TDD katas and dive right in. Don’t get me wrong, code katas are great. They are powerful learning tools. But if you do not know how to drive a car and someone tells you to just get in, start the car, and go, then yes, you might be able to move the car from point A to point B but you might not do it as safely as you should be (letting a light bump of the car in front of you be your guide as to when to stop at a traffic light) or you might be putting a lot more wear and tear on the car than you should be (leaving the parking brake engaged will not hinder your driving much, but you will have to have the brake serviced a lot sooner!), etc.

I think there is a better way. If you are inclined to join me on this TDD journey-in-six-parts, we will start with a blank slate (well, an empty file in Visual Studio) and create one component—using TDD, of course—that is part of a large enterprise application. As simple as it will seem when finished, it really will be a component (a class) that could be part of an enterprise application, not just some toy class. Really. It is, in fact, a component I recently built and included in an enterprise application.

The story you are about to hear is true. Only the names have been changed to protect the innocent.”

TDD == Trepidation?

Unless you have severe centophobia (fear of novelty) or sophophobia (fear of learning) or perhaps even symmetrophobia (fear or symmetry), you should not fear TDD. Remember when you did not have a clue how to do this?

(If WinForms is not your cup of tea, mentally substitute web page or widget or any other GUI platform you like.)You looked at this and thought this is hard. You look at it now and think this is easy!

Well, here you are again at the same point. TDD is hard. Well, it is more that TDD takes some getting used to—along with a whole slew of myriad minutiae—which make it seem hard. This series of articles, if successful, will guide you through the muck and mire to dramatically reduce the slope of your learning curve, to help you achieve what Stuart Firestein, neuroscientist and chair of Biological Sciences at Columbia calls a higher quality of ignorance (see his TED talk The Pursuit of Ignorance). His context was actually the pursuit of knowledge in pure science, where answers are not known, but I think his ideas apply equally well to learning even when the answers are already known… just not by you. “Ignorance” in this context does not refer to stupidity or refusing to accept common truths, but rather it has a strictly positive connotation: referring to gaps in knowledge of an intelligent person.

When you start off knowing little about something—like practicing TDD—you have rather low quality ignorance. Your knowledge is small; your ignorance vast. As you learn more, your ignorance—that is, your questions on the subject—become more refined, more focused, more specific. In short, higher in quality. So my goal here is to convert some of that low quality ignorance to high quality ignorance.

TDD, for those who have not done it, is rather different. Per Grant Lammi, in Dr. Dobb’s Journal:

  • [TDD is] akin to signing your name with the “wrong” hand: it takes more time and concentration, and just feels… unnatural.
  • Writing test code is strange enough… writing it before the application code is…well… downright peculiar.

You just don’t start out with a good comfort level due to unfamiliarity. But consider the potential benefits. TDD:

  1. Promotes better design
  2. Supports validating early and often
  3. Reduces debugging time
  4. Provides documentation as a byproduct
  5. Provides tests as a byproduct

That last one should sound intriguing… consider it a teaser that we will get back to shortly.

Theory and Practice in 5 Minutes

TDD Theory can be boiled down to two things: the Three Laws and the Red-Green-Refactor technique. These are in essence two perspectives on the same thing with a slightly different focus. I like the Three Laws for their specificity on what you should strive to do at any instant, and I like the Red-Green-Refactor technique for forcing you to be conscious about keeping things tidy all the time.

Uncle Bob’s Three Laws:

  • You are not allowed to write any production code unless it is to make a failing unit test pass.
  • You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
  • You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

James Shore’s Red-Green-Refactor:

  • Red: Write a very small amount of test code that fails; most test runners show this with a red mark of some sort.
  • Green: Write a very small amount of production code that makes the failing test pass; most test runners will show this with a green mark.
  • Refactor: Try to improve what you’ve got, not just for that one test but step back a bit typically to look at all the tests in the test class, or the code in the class under test, or occasionally even broader, depending on what code smells you detect.

The critical implicit directive in the refactor step is that you want to cleanup, streamline, and otherwise improve your production code while not changing its behavior in any way. Using TDD all of your production code is covered by tests so once you have refactored if all your tests are still green you have satisfied that directive. The other implicit directive here is, as they say in the movie biz, “That’s a wrap. Print it!” In other words, take what you have at this point and preserve it—by committing to source control. That way you can always return to this state of good, working code should your next chunk of work take you down some dark, dingy alley.

A Whopper of a Misnomer

OK, the third thing you should understand before diving in is that Test Driven Development is not at all about testing!

What is TDD, exactly? I think Jack Sparrow said it best in his well-known talk on software methodology ;-) using this nautical analogy:

Wherever we want to go, we go. That’s what a ship is, you know. It’s not just a keel and a hull and a deck and sails. That’s what a ship needs. But what a ship is…what the Black Pearl really is…is freedom.”

Similarly, TDD is the freedom to (per George Dinwiddie, Why I Practice TDD, 2013.07.23):

  • start working toward your solution before you can map it all out.
  • clear your head of the details because the tests are keeping track of them.
  • make changes knowing your tests will alert you if you violate a previous assumption that you have forgotten.
  • deliver code you can trust to work the way you think it does.
  • know you can correct an error without rewriting all the parts that do work.

That’s a useful start, but it even more useful to consider this: with all this talk about testing and the prevalence of the word test all over the place—even in the name—it is easy to get the idea that Test-Driven Development is about, well, testing. Scott Bellware, in Behavior-Driven Development, clarifies this well:

Folks who do TDD for a while typically come to the conclusion that the only thing that TDD has to do with testing is the appearance of the word test in its name. That’s a bit of an exaggeration, but it’s a common one used by TDD’ers to help clarify the issue. We use unit testing frameworks to do TDD, and we write the example code in methods that can be executed by the test runners that come with unit testing frameworks, but that’s about the end of the commonalities between TDD and testing… TDD’s essential goal is to design and specify the behaviors of the system according to the expectations of the customer, and to be able to consistently prove that those behaviors work according to those expectations for as long as the product lives.”

Scott Bain, in Overcoming Impediments to TDD, adds:

The entities we write are not actually tests. They are specifications. What we are doing is replacing traditional specs with automated specs. The process of writing the specification is an analysis task, one that leaves behind a suite of tests as a side-effect artifact...”

And finally, Uncle Bob (from Agile Software Development) chimes in with:

The act of writing a unit test is more an act of design than of verification. It is also more an act of documentation than of verification. The act of writing a unit test closes a remarkable number of feedback loops, the least of which is the one pertaining to verification of function.”

Behavior-Driven Development

To my mind, Test-Driven Development is easier to understand, manage, and use, when you think of it as Behavior-Driven Development. That is, you are not writing tests: you are writing (verifiable) behaviors. Originally developed by Dan North as a response to issues encountered teaching TDD, BDD typically has a broader scope. It typically involves business analysts, users, and user stories. But using it at a more fine-grained level, as a developer in a more narrow focus, I find quite useful.

With BDD, according to Dan North in Introducing BDD, the challenging questions of TDD become straightforward:

  1. Where to start?
  2. What to test/not to test?
  3. How much to put in one test?
  4. What to name a test?
  5. How to understand why a test fails?

Wrestling with these questions is what makes TDD seem to have such a steep learning curve. But if you think in terms of behaviors… not so much. Keep these questions in mind as we delve into the actual code construction in part 2!

The Tools

Test Driven Development can be done in most any language, and each language will have its own tools and techniques to support TDD. The code we will build is in C# using Visual Studio 2013. Even with that specificity, there are many choices you might make on specific tools. As we proceed in subsequent installments, I am going to introduce tools that I prefer, but in most cases you should be able to easily swap in your own favorite without any issue. Here are the main tools you will see me using, with other popular alternatives.

Category

Tool

Alternative Tools

Purpose

Mocking Framework

Moq

NSubstitute

RhinoMocks

Facilitates isolating a class under test.

Code Generation

ReSharper

CodeRush

Let’s you focus on what you need to code rather than the mechanics and syntax of that code, by letting you add the next relevant chunk of code or code construct often with a single keystroke.

Refactoring

ReSharper

CodeRush

Similar to code generation, refactoring tools often let you perform a complex refactoring with a single keystroke.

Unit Test Assertions

NUnit

Moq

MSTest

FluentAssertions

Though MSTest is built-in to Visual Studio, NUnit provides a richer assertion language so I use NUnit for everything except the assertions more appropriately handled by the mocking framework itself (e.g. verifying that a method on a mock was or was not invoked).

Unit Test Runner

NCrunch

ReSharper

CodeRush

NUnit (Test Adapter)

MSTest

Mostly I use NCrunch for running test because it runs and reports on your tests as you type your code.

Code Coverage

NCrunch

NCover

dotCover

As a byproduct of its real-time testing, NCrunch also annotates your source code for code coverage in real-time.

You will see how these all come into play in subsequent parts of this series. Beginning with the next installment, we dive right in to creating code from scratch using Test-Driven Development. As we go I will introduce the tools above along with design patterns and other techniques that will streamline your development process. There are a lot of pieces to mentally juggle, particularly when you are first starting out, but I believe that learning something then seeing it applied immediately, will help you keep everything organized in your head.

Michael Sorens

Author profile:

Michael Sorens is passionate about software to be more productive, evidenced by his open source libraries in several languages (see his API bookshelf) as well as SqlDiffFramework (a DB comparison tool for heterogeneous systems including SQL Server, Oracle, and MySql). With degrees in computer science and engineering he has worked the gamut of companies from Fortune 500 firms to Silicon Valley startups over the last 25 years or so. Current passions include PowerShell, .NET, SQL, and XML technologies (see his full brand page). Spreading the seeds of good design wherever possible, he enjoys sharing knowledge via writing (see his full list of articles), teaching, and StackOverflow. Like what you have read? Connect with Michael on LinkedIn and Google +

Search for other articles by Michael Sorens

Rate this article:   Avg rating: from a total of 27 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: More
Posted by: MIchael (not signed in)
Posted on: Sunday, July 27, 2014 at 5:38 PM
Message: When are you going to publish the rest of these articles?

Subject: What it's really about
Posted by: Anonymous (not signed in)
Posted on: Thursday, July 31, 2014 at 6:45 AM
Message: Kudos. This is one of the only articles on TDD I've ever seen that actually GETS one of it's major benefits -- forcing you to think closely about the specifications or at least be aware of them before you write the code so you are primed to write the correct code.


Subject: @Michael
Posted by: Andrew Clarke (view profile)
Posted on: Friday, August 1, 2014 at 7:09 AM
Message: Click on the links at the beginning of the article or look in the 'Related Articles' box at the right-hand side of the article . They will get you to the rest of the series.
We decided to publish all these articles together since only the first article stood alone. They ought to be read in sequence to get the most value from them.

Editor

Subject: Tooling
Posted by: ThatGuyDuncan (view profile)
Posted on: Thursday, August 14, 2014 at 10:50 AM
Message: I used MSTest and discovered a few places where NUnit appears superior (It.IsAny<T> would be a joy). I refactored in different places than you did, but we ended up at pretty much the same place.

I have shared this series with peers and I hope they read it.

Subject: Unit Testing Frameworks
Posted by: Reese (view profile)
Posted on: Tuesday, August 26, 2014 at 10:33 AM
Message: Have you seen xunit? http://xunit.github.io/ It was written by the original inventor of NUnit v2. Personally I prefer it to nUnit for C# testing.

Subject: You talkin' to me?
Posted by: ThatGuyDuncan (view profile)
Posted on: Tuesday, September 2, 2014 at 8:55 AM
Message: @Reese, was that directed at me? Prior to your post I had not bothered to look at xUnit, but I now I have -- I would probably enjoy using it.

I am new with my current team and they don't even use MSTest, so any framework at all is going to be a sales job. With NUnit I can confidently say that there are frequent updates and a large community for support -- can I say the same of xUnit? It looks easy-to-learn, easy-to-use and slightly more terse, but with NUnit and Fluent Assertions how much benefit will my team see from xUnit over NUnit?

When I first dived into unit testing, I found that MBUnit was a superior framework to NUnit, but documentation was sorely lacking, so I couldn't recommend it. I see MBUnit is now officially dormant, so I am cautious about recommending anything but the most popular framework.

More research ahead for me! :)

Subject: Not necessarily you
Posted by: Reese (view profile)
Posted on: Tuesday, September 2, 2014 at 9:06 AM
Message: No just the community in general, I think NUnit is great and have used it successfully in development but I prefer the style and ideas that xUnit has taken. Plus the authors have learned from previous mistakes, etc. and can take advantage of newer technology now. http://xunit.codeplex.com/wikipage?title=WhyDidWeBuildXunit

Subject: Very Helpful
Posted by: dcook1021 (view profile)
Posted on: Wednesday, November 12, 2014 at 11:13 AM
Message: Thanks, this article was very helpful and easy to follow. At one point, I felt like the tests were overlapping strangely, but that was an intentional example of choosing better tests. The focus on behavior (rather than just testing a bunch of random functionality) and eliminating redundant or “bad” tests was very helpful, and so was the explanation for the difference between a mock, stub, dummy, and fake.

 

Top Rated

Building a Customised ALM Platform with TFS and VSO
 The latest versions of Team Foundation Server are not only sophisticated, but extensible. Continue... Read more...

ASP.NET SignalR: Old-fashioned Polling, Just Done Better
 A website often needs to update a page as the underlying data changes. You can, of course, just poll... Read more...

Rethinking the Practicalities of Recursion
 We all love recursion right up to the point of actually using it in production code. Why? Recursion... Read more...

Acceptance Testing with FitNesse: Multiplicities and Comparisons
 FitNesse is one of the most popular tools for unit testing since it is designed with a Wiki-style... Read more...

Acceptance Testing with FitNesse: Documentation and Infrastructure
 FitNesse is a popular general-purpose wiki-based framework for writing acceptance tests for software... Read more...

Most Viewed

A Complete URL Rewriting Solution for ASP.NET 2.0
 Ever wondered whether it's possible to create neater URLS, free of bulky Query String parameters?... Read more...

Visual Studio Setup - projects and custom actions
 This article describes the kinds of custom actions that can be used in your Visual Studio setup project. Read more...

.NET Application Architecture: the Data Access Layer
 Find out how to design a robust data access layer for your .NET applications. Read more...

Calling Cross Domain Web Services in AJAX
 The latest craze for mashups involves making cross-domain calls to Web Services from APIs made publicly... Read more...

10 Reasons Why Visual Basic is Better Than C#
 After having converted a whole lot of training materials based on VB.NET into C#, Andy ‘Wise Owl’ Brown... Read more...

Why Join

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.