Why you should make your tests fail

cathalmacdonnacha

Cathal Mac Donnacha 🚀

Posted on December 2, 2021

Why you should make your tests fail

Let's face it, most of us developers don't necessarily love writing tests. We sometimes end up rushing through them, and once we see that green tick next to a passing test, we're generally pretty happy to move on. However, an enemy is lurking amongst us.

False positive test

The enemy I'm talking about here is otherwise known as a false positive test. Let's take a look at what this beast looks like.

Here we have a select element with some countries as options:

<select>
  <option value="">Select a country</option>
  <option value="US">United States</option>
  <option value="IE">Ireland</option>
  <option value="AT">Austria</option>
</select>
Enter fullscreen mode Exit fullscreen mode

Here's my test:

it('should allow user to change country', () => {
  render(<App />)
  userEvent.selectOptions(
    screen.getByRole('combobox'),
    screen.getByRole('option', { name: 'Ireland' } ),
  )
  expect(screen.getByRole('option', { name: 'Ireland' })).toBeInTheDocument();
})
Enter fullscreen mode Exit fullscreen mode

The test passes, isn't that great? ✅ I'm afraid not. 😭  Let's see why after we intentionally make it fail.

Making your test fail

Here's a real example of a false positive test situation I ran into recently:

it('should allow user to change country', () => {
  render(<App />)
  userEvent.selectOptions(
    screen.getByRole('combobox'),
    screen.getByRole('option', { name: 'Ireland' } ),
  )

  // Changed expected country from "Ireland" to "Austria" - this should fail.
  expect(screen.getByRole('option', { name: 'Austria' })).toBeInTheDocument();
})
Enter fullscreen mode Exit fullscreen mode

I was expecting the check for "Austria" to fail because it wasn't the selected country, and I was pretty surprised to see that it was still passing. Looks like we have just identified a false positive test.

Let us take a step back. The purpose of my test is to ensure that when changing a country, it is indeed the now selected option. However, after debugging for while I eventually realised that the test above only checks that the country "Ireland" exists, instead of checking if it's selected.

Here's how I eventually fixed it:

it('should allow user to change country', () => {
  render(<App />)
  userEvent.selectOptions(
    screen.getByRole('combobox'),
    screen.getByRole('option', { name: 'Ireland' } ),
  )

  // Now checking if the option is selected
  expect(screen.getByRole('option', { name: 'Ireland' }).selected).toBe(true);
})
Enter fullscreen mode Exit fullscreen mode

Now, I am correctly checking that the option is selected and all is good. I wouldn't have found this unless I intentionally made my test fail, so I'm glad my persistence has paid off and I avoided a potential bug.

Final thoughts

I've been burnt enough times in the past by false positive tests that I have vouched to always intentionally make my tests fail before moving on to the next one. Since doing this, I've gotten a lot more confident in my tests knowing that they'll only pass in the correct circumstances.

That's about all I have to share with you today. Let me know in the comments if you found this article useful. 🙌

Want to follow along?

I mainly write about real tech topics I face in my everyday life as a Frontend Developer. If this appeals to you then feel free to follow me on Twitter: https://twitter.com/cmacdonnacha

Bye for now 👋

💖 💪 🙅 🚩
cathalmacdonnacha
Cathal Mac Donnacha 🚀

Posted on December 2, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related