Why you should make your tests fail

Why you should make your tests fail

Learn why making your tests fail is actually a good idea.

ยท

3 min read

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>

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();
})

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();
})

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);
})

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 ๐Ÿ‘‹

ย