Tried TDD and didn't realize the benefits? Try it the next time you get writer's block

Corey Cleary - Sep 21 '18 - - Dev Community

Originally published at coreycleary.me. This is a cross-post from my content blog. I publish new content every week or two, and you can sign up to my newsletter if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets, links to other great tutorials (by other people), and other freebies.

Have you ever tried Test-Driven Development (TDD) thinking it would be the "holy grail" it's often made out to be, only to end up feeling like it was pointless?

Maybe it didn't add any benefit to your code. Maybe writing your test first, then the code after felt uninspiring or limiting, or just the wrong way to do things, especially since the way programming is taught is code-first, not the other way around. Or maybe it just felt like a chore.

All the best developers seem to talk about TDD like it's the only way to code, and if you're not doing it that way, you're wrong. So you really want to like it. But if you've tried it and you didn't like it for any of the multitude of possible reasons, what is the point in practicing it at all? If only you could have that "aha moment" that made TDD make sense, you might actually enjoy it and feel like a "real developer."

I think the adoption of TDD is more often than not encouraged in the wrong way, and I'm going to show you how to think about it in a different way that will help you realize the benefits more quickly. Not because industry knowledge dictates it's what you're "supposed to do" or your team looks down on you if you don't develop that way, but because it can be one of the best tools in your toolbox to help you when you get stuck.

How it's usually encouraged

Much has been written about the value of TDD (writing your tests first, then writing the code). The usual benefits touted by TDD-adoption are:

  • less bugs
  • faster overall delivery
  • smaller, single-responsibility functions

Less bugs, faster overall delivery, smaller functions - awesome. Some developers/teams really struggle with this, and for them the benefits will probably click more easily. But it still may not make sense to you why you should do it if you don't have many bugs or problems delivering code quickly and your functions are single-responsibility already.

And the "why you should do TDD" argument as above, while certainly developer-oriented (especially the last bullet), is more targeted at management. I've seen managers who haven't coded in forever, if at all, herald TDD as the "fix-all", suddenly mandating it as the new development-style, which ends up turning it into something that's been chosen for you, rather than something you've chosen. This doesn't help.

Thus, TDD can become something you feel you should do because:

  • Your boss told you to
  • The industry tells you to
  • You're looked down upon by your peers if you don't
  • You look down on yourself if you don't

Or maybe you don't have any of these pressures at all - you just don't get TDD. Maybe the long-term benefits of less bugs and easier to read/write functions is just too abstract right now.

But attempting to adopt TDD with this mindset, it can turn TDD into more of an adversary as opposed to something you do because it helps you - you as a developer who is under the gun to deliver on a feature.

Something more relatable

Instead of understanding TDD from a "best practices" perspective, I've found it easier to understand in more concrete terms, something more relatable.

As stated in the title of this post - to get TDD to "click" try using it the next time you are faced with writer's block, called "coder's block" from here on.

What is coder's block?

Have you ever been in a situation where you've been completely stuck trying to figure out how you're going to implement a particular piece of code? Maybe a deadline is approaching and you're really screwed if you don't get that code written, but you keep hitting coder's block and don't know how to start. Before I started using TDD to push through these block I used to just browse Reddit, HackerNews, etc. as a way of procrastinating. Either I was overwhelmed by a really hard problem and I didn't know where to start breaking it down, or it was just one of those days.

While "best practices" is abstract, I bet you've encountered coder's block lots of times. But you can use TDD here to help you out of that situation. Not because someone told you you're not a good developer if you don't, but because it helps you.

Side note: I'm not a TDD-purist. I understand it doesn't always make sense to write tests first (R&D work, initial proof-of-concepts/sketches, pure DOM/view code, etc.). But TDD as removing writer's/coder's block has been invaluable for me, which is why I recommend it here.

How to do TDD next time you get stuck

In order to demonstrate how you'd go about this, let's imagine a simplified scenario. You have a feature for an online shopping application you're working on in which the requirements are:

  • Customer must be able to enter their preferences under a "Profile" tab
  • Customer preferences must be saved
  • Preference input fields must match some regex

Imagining you're stuck and aren't sure where to start, you could think about what the very first test you could write would be.

There are several requirements here, but you know you've got to manage the state of the selected/entered preferences, so that's a good place to start. The test, assuming the application is in JavaScript, might look like:

import {addPreferences} from '../preferences/preference.service'

import {Preferences} from '../preferences/preference.service'

let pref_service

describe('PreferenceService', () => {
  beforeEach(() => {
    pref_service = new Preferences()
  })

  it('should initialize state', () => {
    expect(pref_service.preferences).to.deep.equal({
      contact_method: null,
      phone_number: null,
      email: null,
      preferred_shipping: null
    })
  })
})
Enter fullscreen mode Exit fullscreen mode

This might not seem like much, but it's actually quite a lot. We've already figured out what shape our state/preferences need to be in, which is a meaningful part of the implementation. And more importantly, we began by not knowing where to start at all.

An example implementation of the code for that test might be:

export class Preferences {
  constructor() {
    this.preferences = {
      contact_method: null,
      phone_number: null,
      email: null,
      preferred_shipping: null
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Cool, now another test:

it('should add preference to preference state', () => {
  pref_service.setPreferences({phone_number: 'phone-number'});
  expect(pref_service.preferences).to.deep.equal({
    contact_method: 'phone-number',
    phone_number: null,
    email: null,
    preferred_shipping: null
  })
})
Enter fullscreen mode Exit fullscreen mode

And that code:

setPreferences(preference) {
  this.preferences = Object.assign(this.preferences, preference)
}
Enter fullscreen mode Exit fullscreen mode

Start with one unit test, then the code. Another test, another piece of code. Now you're probably already over that block you had when you started.

Wrapping up

Thinking about TDD in this way will hopefully help you realize the power of it. A lot of getting TDD to "click" is getting into a rhythm. And more importantly, using it as tool to help you, not something that's a "best practice" you're following.

When you get going and get over that block it will start to make more sense. Just like how you break something down by writing a todo list, then you do the things on that list - using TDD to overcome coder's block and seemingly overwhelming features is the same mechanism.

This will ultimately be what makes you a better developer - overcoming blocks by learning to understand requirements and breaking down the problem into manageable parts. Not only will you spend more time coding - which itself will make you a better developer - but you'll know how to make things manageable.

The next time you're stuck try writing just one test before you write the code. Only one. Even by figuring out a starting point, this will greatly help in getting unstuck and give some direction, and even if you don't use test-first after those first few tests, you'll have figured out a path to implementation.

I think testing should be as easy as possible in order to remove the barriers to actually writing them. It's one thing to get stuck on code - you don't have any choice but to fix it. But its another thing to get stuck on tests - with tests you technically can skip them.

I'm trying to make testing and other things in JavaScript easier by sending out tutorials, cheatsheets, and links to other developers' great content. Here's that link again to sign up to my newsletter again if you found this post helpful!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .