This series goes through anti-patterns when writing tests. Yes, there are and will be many.
TDD without refactoringLogic in testsMisleading testsNot asserting
Code matchingData transformationAsserting on not nullPrefixing test names with "test"

Logic is everything that causes two or more possible paths. For example, if-else or switch-case, or similar. It’s good in production code, less so in unit tests.

Consider a widget class that can connects to a third-party system. Only, it needs a third-party system to connect to, which sometimes may not be possible. Let’s say we want to test the connection status in both states.

@Test
public void widget_connectionStatus()
{
	widget widget = new widget();
	if (testConfig.inRealMode) {
		assertFalse(widget.isConnected());
	}
	else {
		widget.connectToThirdPartySystem();
		assertTrue(widget.isConnected());
	}
}

Looks weird, right? But not as uncommon as you think.

The testConfig is used as a switch to run tests based on configuration (obviously). These are usually environment switches. It could be a real connection, or another setup. We can also use the configuration not just for different operations with the object . We can also set up different mock responses. The possibilities are endless.

It may not be a “real” unit test, but that’s not the point.

What’s wrong with this picture?

The main reason we’re staying away from logic in tests, is that logic is a petri dish for bugs. The reason we’re testing in the first place is to make sure code that contains logic works. Adding logic to tests is like inviting a vampire into your home. He’ll gladly accept your invitation, and now you’re in trouble.

Bugs in tests are dangerous. Buggy code? We’re used to that. In fact, we build a safety net of tests because we trust that our tests will tell us if the code is broken. But now, if our tests have potential errors – how much trust can we put in our them?

And you know what comes next – we’re only writing tests because we can trust them. What’s the point of writing them, without that trust?

So yeah, you should stop writing tests with logic in them, and when in doubt, separate into different logic-less tests.

We’re not done, though. There’s a deeper issue here, which goes into understanding coding.

Why would you write a single test with both and “if” and an “else” in the first place?

I imagine the process goes like this: you write a regular test for the “if” clause:

@Test
public void widget_connectionStatus_RealMode()
{
	widget widget = new widget();
	assertFalse(widget.isConnected());
}

But then decide to add the “else” clause to cover the other part of the code, because you’ve found the perfect place for it – all the setups are done, no need to copy everything (like that’s actual work).

It’s the efficiency mindset of solving the short-term issue, at the expense of the long-term maintainability. If the coding standards are poor, the tests will be up to those standards as well.

Tests are great for finding smells in your code. In more ways than one.

Check out my workshops, where I discuss patterns and anti-patterns in unit tests.

Categories: anti-patterns

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *