The TDD Guy

Adventures in Test-Driven Development!

Find Ed on GitHub!

CopperStarSystems (Ed Mays)
Copper Star Systems, LLC
Phoenix, AZ
https://www.codementor.io/copperstarconsulting
Joined on Jul 10, 2013
40 Public Repositories
AspNet-DataAccess-Example
AspNet.WebApi.Example
AspNetCore20ViewComponents
AzureFunctions.Sample
basic-mvvm-samples
bootstrap-grid-example
bootstrap-modals
CopperStarSystems.WindsorHelpers
DataTemplating-With-Triggers
DelegatesExample
DependencyInjection.GettingStarted
dev-spaces
DispatcherTimerTestingExample
dotnetcore-sample
ElmahExample
EventsExample
exploring-wcf
handlebars-task-list
international-phone-number.poc
jquery-ajax-example
mono
MVC-StudentPairingExample
mvc6-view-components
mvvm-drag-drop
MvvmMasterDetail
NetCore.Globalization.Example
postsharp-example
SelfBootstrappingAssembly
serilog-sinks-datadog-logs
tdd-hello-nunit
9 Public Gists
You are here: Home

Adventures in Test-Driven Development

Since 2005, Test-Driven Development (TDD) has been a vital tool in my bag of tricks for ensuring high-quality, robust code. Since then, I have applied TDD principles on multiple projects and teams with mostly successful outcomes.

When I first began investigating TDD, my progress was stymied by a lack of learning resources, causing me to make many missteps. I also lamented the lack of a resource that tied together all of the pieces, showing them as a whole - most of the available examples were limited to "Hello World" scenarios and tapered off afterwards. Over the years, I gained a more holistic understanding of TDD, including best practices, standards, and tools.

My goal with this blog is to share my experiences, both good and bad, with TDD in the hopes that it helps others understand and master this powerful tool.

Moq 102: MockRepository and More

January 19, 2017 by Ed Leave a Comment

So let’s just dive in today.  Our current test class is fairly clean and straightforward, but we’re not using all of Moq’s capabilities yet.  One notable thing is that we have to remember to call VerifyAll on all of our individual mocks.  Fortunately, Moq provides us with a tool to mitigate this problem:  MockRepository is a class we can use to create and track mocks, and verify all expectations at once.

So how does MockRepository work?

Continue Reading

Refactoring for Testability: Factories for Creation

January 18, 2017 by Ed Leave a Comment

Picture of a factory
Factories are for building things.

In the real world, Factories manufacture items for output.  In software engineering, Factories create object instances for us so we can avoid using the “new” operator.  Factories also give us substantially more control when testing, and can even provide a means of verifying “Black box” operations.

Why would we want to avoid the “new” operator?  Isn’t that what it’s there for?

Well, yes, but newing up a dependency within a class goes against the concept of dependency injection.  Thinking back to our ReallyHardToTest class, we know we’re creating a new instance of SomeModel in the constructor, and one of our methods provides a way to change the SomeModel instance.  The problem is that since we create the instance within ReallyHardToTest, we cannot control it or even verify that an instance is created.  Furthermore, we can’t verify that the SetFilename method actually does what it says on the tin because the SomeModel instance isn’t exposed publicly by that class.

Factories Help Us Build Things

At its root, the factory pattern (there are actually a few variants) encapsulates the logic for creating instances of particular types.  When employing these patterns, now dependent classes can have a factory injected, and then ask the factory for a new instance when they need it.  Since the factory is injected, we can control it at test time – we even get to control the instance returned by the factory!

In advanced scenarios, for example when a factory will be invoked repeatedly, we can use advanced features of Moq, including SetupSequence() to return different instances in a specific order.

Continue Reading

Refactoring for Testability: Program to Abstractions

January 17, 2017 by Ed Leave a Comment

 

abstraction picture
Abstraction

Programming to Abstractions is a another common architectural pattern that facilitates unit testing.  Abstractions provide a way for us to decouple specific implementation details from the consuming class.  Another benefit of abstractions is that we can substitute alternate implementations if needed – something that is very difficult to do otherwise.

Let’s start by looking at our trusty ReallyHardToTest class that we’ve been working with:

Let’s focus on the ReallyHardToTest constructor, which takes an instance of SomeOtherDependency.  We know from prior tutorials that we can’t mock concrete types, but we want to make sure that the ReallyHardToTest constructor invokes SomeOtherDependency.DoSomething().  So how can we make this more testable?

Continue Reading

Refactoring for Testability: Remove Static Dependencies

January 16, 2017 by Ed Leave a Comment

Challenge:  Classes with Static Dependencies are Difficult to Test

Static Dependencies pose a set of challenges to testability, most notably:

  • Static Dependencies cannot be mocked
  • Due to their nature, static dependencies are rarely injected so they are hidden dependencies

In my prior post, I showed a very difficult-to-test class that uses the static System.IO.Path.GetFileName method.  Today, we’ll do some refactoring on that class to make it more testable!

Continue Reading

Battling Complexity: Tell It to the Monkey

January 15, 2017 by Ed Leave a Comment

My students often ask me “How can I tell if my code is too complex?”  There are a lot of different answers to that question, but I thought I would share my favorite complexity detection tool with you – a tiny purple stuffed monkey:

Manage complexity by explaining your code to an inanimate object
Look deep into my eyes and explain the code…

I adopted the idea from a great concept called “Rubber Duck Debugging” that I read about in the phenomenal book “The Pragmatic Programmer”.  I’m not really a duck guy so I went with a monkey instead.

The basic concept behind it all is to explain your code to your duck/monkey/other inanimate object, step by step.  If it takes you longer than 1-2 minutes to explain a method, it’s probably too complex and should be refactored to something simpler.

Well that sounds great and all, but how exactly do I reduce a method’s complexity?

Good catch – “Just simplify it” is unacceptably hand-wavey and doesn’t really provide much guidance.  One good approach for reducing complexity is called the Single Level of Abstraction Principle (SLAP).  As might be apparent from the name, SLAP recommends that all statements within a given method exist at the same “Level of Abstraction”.

OK, but what’s a Level of Abstraction?

Continue Reading

Fail Proudly!

January 14, 2017 by Ed Leave a Comment

Nobody likes to fail.  Such a simple word implies so many negative connotations:  Laziness, incompetence, unprofessionalism, and more.  With that in mind, it’s interesting to consider the fact that Unit Tests are all about failure!

Fail Early, Fail Hard

You might be scratching your head at this point, wondering what I mean by that.  Aren’t unit tests supposed to pass?  As usual, the answer is a bit deeper than the surface.  Much like the canaries that miners used to carry with them to detect noxious gases, unit tests serve as an early indicator that something is wrong.  In a traditional TDD workflow, failing tests are a normal occurrence (in fact, with strict TDD, you start with a failing test).  The test runner makes failures clearly visible after each test run.  This is a good thing because it reduces the likelihood of overlooking a failing test case.

In fact, unit testing’s primary value comes from failing tests, detecting bugs as early as possible in the development cycle.  Multiple studies have shown that the cost of fixing software bugs rises dramatically the later the defect is discovered.  To illustrate, let’s look at a hypothetical example, imagining a team working on a commercial software product when a bug is introduced:

Continue Reading

Writing Testable Code Part 1: Understanding Testability

January 14, 2017 by Ed Leave a Comment

Testability is a key concern when following TDD practices. There are many different ways to accidentally write difficult-to-test code, but fortunately they tend to fall into fairly broad categories. In this post, we’ll examine some common coding pitfalls that introduce testing challenges, and future installments will discuss ways to work around or avoid these issues.

So What Do You Mean By Testability?

Testability is a measure of how easy (or difficult) it is to write tests for a given class.  There are several development practices and patterns that work together to improve a given class’ testability, including Dependency Injection and SOLID Principles.  In general, highly-testable classes will have several traits in common:

  • Dependencies are specified as abstractions (either interfaces or abstract classes)
  • Dependencies are injected rather than created internally
  • Factories are used to create objects with runtime dependencies
  • Avoidance of static classes and methods wherever possible

So let’s take a quick look at a class that is inherently difficult to test and identify the various problems…

Continue Reading

Adopting TDD Processes: Change is Hard

April 26, 2015 by Ed Leave a Comment

Over the years I have introduced TDD practices to multiple teams, both as an engineer and as a consultant.  Often I find that teams have some awareness of TDD process and tools, but have fundamental misunderstandings about the process and its benefits.  The initial perception that I generally see is that TDD adds work to the development cycle while adding minimal benefit.  In fact, although adopting TDD requires some time investment, the final benefits far outweigh the initial pain points.

In a way, the process of adopting TDD is very similar to committing to a fitness goal:

Continue Reading

TDD vs Non-TDD: A Hypothetical Scenario – Part 4: The Rest of the Story

April 18, 2015 by Ed Leave a Comment

So now we come to the true confessions part of the story…

Continue Reading

TDD vs Non-TDD: A Hypothetical Scenario – Part 3: TDD Environment

April 7, 2015 by Ed Leave a Comment

You calmly lean back in your chair for a moment, taking in the news.  “Wow, this is a pretty big change at this point in the release cycle, but I’m sure Management has considered those risks and the potential for a negative impact.  They’ve committed to this course of action?”  “Yes, and they’re not willing to make any compromises at all – this is a mandate.” says the PM.

For a brief moment, your mind flashes back to the meetings from last year when the team discussed development process, coding standards, and other related practices, unanimously voting to adopt TDD.  You also recall standing your ground in a few conversations with upper management, helping them understand the value of TDD despite their initial impression that it was just busywork.  Your mind then leaps to memories of the initial implementation, how the team had to expend additional effort building test infrastructure, shift their mindset to consider testability, and fully commit to architectural standards.  At first, you recall, the learning curve was brutal but that effort paid rich dividends as the project matured, requiring less and less testing infrastructure while still providing appropriate code coverage.  “And now,” you muse to yourself, “we’ll get to demonstrate to business and management how TDD also mitigates risk from unexpected changes.”

Continue Reading

  • 1
  • 2
  • Next Page »

© 2021 · The TDD Guy