Include Only Relevant Details In Tests

This article was adapted from a Google Testing on the Toilet (TotT) episode. You can download a printer-friendly version of this TotT episode and post it in your office.

By Dagang Wei

What problem in the code below makes the test hard to follow?

def test_get_balance(self):

  settings = BankSettings(FDIC_INSURED, REGULATED, US_BASED)

  account = Account(settings, ID, BALANCE, ADDRESS, NAME, EMAIL, PHONE)

  self.assertEqual(account.GetBalance(), BALANCE)

The problem is that there is a lot of noise in the account creation code, which makes it hard to tell which details are relevant to the assert statement. 

But going from one extreme to the other can also make the test hard to follow:

def test_get_balance(self):

  account = _create_account()

  self.assertEqual(account.GetBalance(), BALANCE)

Now the problem is that critical details are hidden in the _create_account() helper function, so it’s not obvious where the BALANCE field comes from. In order to understand the test, you need to switch context by diving into the helper function.

A good test should include only details relevant to the test, while hiding noise:

def test_get_balance():

  account = _create_account(BALANCE)

  self.assertEqual(account.GetBalance(), BALANCE)

By following this advice, it should be easy to see the flow of data throughout a test. For example:

Bad (flow of data is hidden):

Good (flow of data is clear):

def test_bank_account_overdraw_fails(self):

  account = _create_account()

  outcome = _overdraw(account)

  self._assert_withdraw_failed(

    outcome, account)


def _create_account():

  settings = BankSettings(...)

  return Account(settings, BALANCE, ...)


def _overdraw(account):

  # Boilerplate code

  ...

  return account.Withdraw(BALANCE + 1)


def _assert_withdraw_failed(

    self, outcome, account):

  self.assertEqual(outcome, FAILED)

  self.assertEqual(

    account.GetBalance(), BALANCE)

def test_bank_account_overdraw_fails(self):

  account = _create_account(BALANCE)

  outcome = _withdraw(account, BALANCE + 1)

  self.assertEqual(outcome, FAILED)

  self.assertEqual(

    account.GetBalance(), BALANCE)

def _create_account(balance):

  settings = BankSettings(...)

  return Account(settings, balance, ...)


def _withdraw(account, amount):

  # Boilerplate code

  ...

  return account.Withdraw(amount)