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.
Published on Java Code Geeks with permission by Ashley Frieze, partner at our JCG program. See the original article here: It Looks Right To Me Opinions expressed by Java Code Geeks contributors are their own. |