Home

Writing good tests

Context: I'm currently working with a good friend and competent programmer who hasn't written a lot of tests. this blog post is dedicated to you 🙂

Good tests should have the following attributes (among other things):

  • readable
  • break when the business logic being tested is broken (i.e. no false positives)
  • not break when the business logic being tested is not broken (i.e. no false negatives)

Here are a couple of rules for writing good tests

Rule 1: Test the code, not the data

How do you know you are testing the code and not the data? your tests should pass regardless of the content of the database (there are edge cases: I don't expect your tests to pass if your database is empty). Let's look at an example.

// bad
describe('search products method', () => {
	test('calling with "foo" should return products with name "foo"', () => {
        expect(search("foo")).toEqual({id: "xyz", name: "foo", price: 10})
	})
})

This is bad because your test depends on the state of the database. It will break when you change the price of product foo or if you have more than 1 product with the name product "foo".

// good

describe('search products method', () => {
	test('calling with "foo" should return products with name "foo"', () => {
        const foo_products = search("foo")
        const has_name_foo = o => o.name === 'foo'
        expect(foo_products.every(has_name_foo)).toBe(true)
        // guarding against edge case where there are no products in db
        expect(foo_products.length).toBeGreaterThan(0)
	})
})

This is better because we don't rely on the db having only 1 foo product

That's it for now. I'm tired. In an upcoming blog post I will cover the rest:

  • Rule 2: Test the intent, not the implementation
  • Rule 3: Writing tests is implementing the test description (not more or less)