Testing the Easter program—part 2

FAQ on the testing code

You might have some questions upon seeing the tests I wrote for my Easter program (I include these tests for reference at the end of this post).

There are only seven tests. Are they enough?

Maybe. To be exhaustive we’d need to write 5.7 million tests (Easter dates repeat after that, or so they say), and most models have infinite cases. Normally what we do is try to catch the edge cases and get going. At some point we may discover a bug in another edge case, so we add another test. And so the story goes.

How do you find the expected result? How do you know that on 1598 the Easter was on 22 March?

In this particular case I used another Easter program (ncal) that I believe to be correct. The person who writes the first such program needs to test it by manually implementing the Church’s method to determine the expected results.

If your model is novel and you have nothing to compare it to, the best way to test the code is to have your associate solve the test case by hand, or in Excel, or in some other way, and sit down together to work out any differences between his results and your results. While this is time consuming, it’s exciting work, and you will have a rewarding feeling of trusting your code (and your model) when you’re done.

Finally, if you have code without automated tests, but you have used it for years and trust it, you can use it to solve the test cases and assume they’re correct. This is useful if you want to refactor the code but are afraid you might break something.

The testing code looks very verbose. Wouldn’t it have been better to create a table of expected results and loop over it to check?

In general no. Tests must be dead simple, without loops or ifs.

In this particular case, I think creating a table and a loop would have made sense. But in the thousands of tests I’ve written, I don’t recall ever having done anything like this. If I had shown you a table, it would have been hard to convince you that this is only a rare exception. I thus wrote the tests in the normal way.

test('earliest possible date', () => {
  expect(getEasterDate(1598)).toEqual([3, 22]);

test('latest possible date', () => {
  expect(getEasterDate(1666)).toEqual([4, 25]);

test('19 April special case where d = 29 and e = 6', () => {
  expect(getEasterDate(1609)).toEqual([4, 19]);

test('non-special 19 April', () => {
  expect(getEasterDate(1615)).toEqual([4, 19]);

test('18 April special case where d = 28 and e = 6 and more', () => {
  expect(getEasterDate(1954)).toEqual([4, 18]);

test('non-special 18 April', () => {
  expect(getEasterDate(1965)).toEqual([4, 18]);

test('correct p', () => {
  expect(getEasterDate(4200)).toEqual([4, 20]);