It Looks Right To Me

When writing a test it’s important to test both positive and negative scenarios. It’s important to test edge cases. However, when choosing test data, the test data needs to illustrate the test case well.

If the test data is hard to connect back to the exact use case, then it can lead someone to misunderstand the test, its implied specification, and the code behind.

Here’s an example where the test data is helping us:

expect(getFieldCaseInsensitive(obj, 'username'))
  .toEqual('Mr User');

expect(getFieldCaseInsensitive(obj, 'Username'))
  .toEqual('Mr User');

expect(getFieldCaseInsensitive(obj, 'UserNaME'))
  .toEqual('Mr User');

These three examples of the field value express case variations in an algorithm where the case is to be ignored.

So what about the case where we test for a field that really doesn’t exist:

// how about...
expect(getFieldCaseInsensitive(object, 'usename'))
   .toBeNull();

// or
expect(getFieldCaseInsensitive(object, 'thisIsNotAField'))
   .toBeNull();

Both are fields that should not exist, but the first one, at a glance, looks like the original field name. It looks nearly right. What’s the significance of the misspelling?

In the context of the above, where there are so many variations of Username in the tests, the slight-misspelling might be easier to notice. Our attention is drawn to spelling and capitalization in this case, owing to the fact that the algorithm is a case insensitive one.

This problem gets harder, though, when the behaviour of the algorithm is simpler, and the variation in test data is really subtle:

it('allows a good login', () => {
   expect(login('?user=67554&pass=p4ssw0rd')).toBeTruthy();
});

it('refuses a bad login', () => {
   expect(login('?User=67554&Pass=p4ssw0rd')).toBeFalsy();
});

At a glance, why is the second one wrong? The algorithm may only be looking specifically for user and pass and we’re not supplying those… so why supply things that look like them to prove the negative case? Unless we’re highlighting some very specific case-sensitivity use case that was supplied by the product team (in which case, the test name needs to explain that), this is just a confusing way of saying:

it('refuses a bad login', () => {
   expect(login('?randomdata=xxxxx&otherdata=wibble')).toBeFalsy();
});

// or

it('refuses a bad login', () => {
   expect(login('')).toBeFalsy();
});

In both the above cases, the required user and pass fields are OBVIOUSLY missing, not nearly present.

TL;DR

Choose a combination of test data and test name that precisely describes the scenario, without accidentally implying an alternative.

Data that looks nearly right should be handled carefully as edge cases, and should be avoided where possible.

One comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s