Archive for October, 2022

Differences between TDD and BDD

So what’s the different between Test-Driven Development (TDD) and Behavior-Driven Development (BDD)? I’ve written about that before, but I think that post lacks something more illustrative.

For one thing, I forgot to mention: I’m in the camp that thinks of the last D as design, instead of development. That matters.

I’ve heard developers say things like:

  • “I don’t do TDD anymore, now I’m BDD all the way!”, or
  • “I only do TDD, because I can’t use Cucumber, SpecFlow, or any one of those BDD tools.”

Here’s how I see it…

At some point, I learned to write unit/integration tests in this manner:

Through TDD, I learned to write such test before writing the actual code.

Then I learned about “BDD-style” tests, so I refactored tests such as the one above into the one below:

That was my transition from Arrange-Act-Assert (AAA) to Given-When-Then (GWT).

Then I learned that BDD and TDD go hand-in-hand:

No special tools, languages, test frameworks. In the example above, just plain C# and xUnit.net.

In summary:

  • BDD specifying the desired outcome
  • TDD specifying the desired approach to achieve the desire outcome
Advertisement

Leave a comment

TDD and Legacy Code

 

Is Test Driven Development (TDD) practical when working with a lot of legacy code?

Yes, it is.

There’s an opportunity to practice Test Driven Development (TDD) whenever we write new code in a legacy system. As an example, when writing a new component, service, feature, etc., which is going to be called from the legacy system.

But what if the new code is to be written right in the middle of existing legacy code?

There’s still an opportunity for TDD.

Think what the new code is supposed to do.

Maybe it’s a new if-block that checks some variables in the existing code. That if-block asks the system a question; could that question be implemented as a method, a function, or similar approach, and then called from the existing code?

Or maybe the new lines will perform some sort of task, which is an opportunity to follow TDD to design and implement the new task, and then call it from the legacy code.

What about testing the existing legacy code?

There’s a possibility that the current state of the legacy code is so poor that even changing it to call any new code is so hard that a decision is made to only change the old code, and therefore, leaving no room for TDD.

If that’s the case, there’s still value in at least trying to write characterization tests for the existing code. That’s one scenario where using code coverage can yield benefits, as opposed to the bad way many people use it.

The existing system may be massive, so it’s important to know what we should write tests for.

What do we get from that?

The experience acquired with writing tests for legacy code makes us stronger TDD practitioners; as we experience the pain of writing tests for code that wasn’t implemented and designed with testability in mind, we also apply those lessons when designing new solutions.

But what if I’m working on a greenfield project and there’s no legacy code here?

Isn’t there? Are you sure? Many developers think of legacy code as code written a long, long time ago, often in defunct languages.

Michael Feathers defined legacy code as “code without tests”.

I’ve learned to think of legacy code as “code nobody wants to deal with, with or without tests”.

Sometimes a decision is made NOT to write tests (topic for another post). We can still write the code in ways that people won’t feel compelled to run away from it, even if there are no tests yet.

Sometimes we write tests within a sprint, but we do a poor job at writing those, so tests become “code nobody wants to deal with”.

In case you haven’t already, make sure to read and apply techniques from Michael Feathers’ Working Effectively with Legacy Code.

Leave a comment

Recommended Reading on Testing

I give many talks on Testing (more specifically, TDD, BDD, unit testing, etc.) and often get asked for recommended reading on the topic.

I’ll list here some of the resources I remember I’ve read. Beware that some of them I’ve read a long time ago and do not know how well they’ve aged.

I’ll focus solely on books. I know I’ve read useful blog posts in the past, but I haven’t kept track of the most relevant for me, unfortunately.

Working Effectively with Legacy Code, by Michael Feathers

Amazon link

I’ve read this book twice. The first time was shortly after it came out in 2004. It had been only a couple of years since I learned about unit tests and had already felt the pain of writing tests for legacy code. I remember the book helped me a lot through period, and many of its lessons stuck with me.

I’ve read it a 2nd time earlier this year (2022) and believe the book has aged well, and think every developer should read it at some point in their career.

“oh, but I don’t work with legacy code”. If that’s you, just know that the code you wrote this morning may already be considered “legacy”.

Agile Principles Patterns and Practices in C, by Robert C. Martin

Amazon link

This book came out in 2006, and that’s when I think I’ve read it. I remember recommending it to many developers for several years afterward.

I loved the book because it covered OOP, Design Patterns, SOLID principles, and TDD, often writing tests before refactoring code that would eventually surface as a pattern or principle.

I have not revisited this book in many years, but I know that a lot of the things I’ve learned from it have stuck with me.

The Art of Unit Testing, by Roy Osherove

Amazon link

I’ve read this book around the same time as the other two mentioned above. The Amazon link is for the 2nd edition, published in 2013, which I haven’t read (I see there’s also a 3rd edition, with samples in JavaScript).

I recall learning things about fake objects (mocks, stubs, spies), and I remember recommending this book to other developers back then. If memory serves me right, it’s a short book to read and it provides good information for those getting started into the practice.

Fifty Quick Ideas to Improve Your Tests, by Gojko Adzic and David Evans

Amazon link

This one I’ve read earlier this year and enjoyed it very much, as it validated many of the lessons I’ve learned and applied over the years, and it also gave me some new ideas to try out.

The RSpec Book: Behaviour Drien Development with RSpec, Cucumber, and Friends, by David Chelimsky, Dan North, and others

Amazon link

I’ve read this book when I moved from .NET to Ruby on Rails in 2011. I found this book as I was digging more into Rails, Cucumber, RSpec, Testing, BDD, and all that stuff, and it helped me at the time.

I am positive that things I’ve learned there were brought over when I came back to .NET years later, and I also apply it to JavaScript, TypeScript, Cypress.io, Jest, etc.

Is that all?

Those are books that first come to mind when people ask. I’m sure I’ve read others in the last 20 years, but I didn’t use to keep track of it they way I have been in the last few years, and whichever other books I’ve read didn’t stick with me.

I’ve put out several blog posts on the topic of testing, documenting my questions, confusions, learnings, etc., and will continue to do so.

I also have a list of books on the topic on my “to read” list, and I’ll write up posts in case the books deserve it. 🙂

While the books mentioned here have helped me along the way, the single most important that shaped my practice has been actually doing it!

,

Leave a comment