Often I find myself writing a unit test for code and repeating some or a lot of the logic in the unit test to make the unit test DRY.
For example, consider the following piece of code:
function speak(conditionOne, conditionTwo) {
if (conditionOne) {
return "Hey"
} else if (conditionTwo) {
return "Sup"
} else {
return "Waddup"
}
}
Now I could write a unit test using jest
, for example, by hardcoding all the cases.
describe('speak', () => {
test('when condition one but not condition two', () => {
expect(speak(true, false)).toEqual('Hey')
})
test('when condition one and condition two', () => {
expect(speak(true, true)).toEqual('
test('when not condition one but condition two', () => {
expect(speak(false, true)).toEqual('Sup')
})
test('when not condition one and not condition two', () => {
expect(speak(false, false)).toEqual('Waddup')
})
})
Or I could try to make it a bit more terse using syntactic sugar but still hardcoding the cases and expected outcomes:
describe('speak', () => {
test.each`
conditionOne | conditionTwo | value
${true} | ${false} | ${'Hey'}
${true} | ${true} | ${'Hey'}
${false} | ${true} | ${'Sup'}
${false} | ${false} | ${'Waddup'}
`('when conditionOne is $conditionOne and conditionTwo is $conditionTwo then $value', ({ conditionOne, conditionTwo, value }) => {
expect(speak(conditionOne, conditionTwo)).toEqual(value)
})
})
But you could imagine if there is a decent amount of logic then the number of cases to hardcode in the table become exponential and the outcome isn't always so easy to hardcode as a value.
This is where I think it might be a good idea to make the test more intelligent, but then it just starts to become a repeat of the unit logic (and what if I got the unit logic wrong?)
For example:
describe('speak', () => {
describe.each`
conditionOne
${true}
${false}
`('when conditionOne is $conditionOne', ({ conditionOne }) => {
test.each`
conditionTwo
${true}
${false}
`('when conditionTwo is $conditionTwo', ({ conditionTwo }) => {
let value
if (conditionOne) {
value = 'Hey'
} else if (conditionTwo) {
value = 'Sup'
} else {
value = 'Waddup'
}
expect(speak(conditionOne, conditionTwo)).toEqual(value)
})
})
This now makes it much easier to test when manually writing out the table would be cumbersome. However, it seems like it is prone to the same bugs as the unit itself? But on the other hand it lets me refactor with confidence and I can see the test cases get printed out and make sure they are OK when the tests get run.