I’m implementing the view transformation part of a graphics pipeline (basically a matrix which translates coordinates from world coordinates to camera coordinates given a camera position and direction).
Other than the simple case where the point has the same coordinates if the camera is at [0, 0, 0] pointing towards [0, 0, 1] I can’t seem to come up with any obvious test cases. Essentially it seems like there’s no way of describing what should happen other than the implementation itself.
In the past, I have found that using a spreadsheet application such as Excel to generate test data has been very useful for testing mathematical code.
Writing the same mathematical model twice using two different tools (a programming language and a spreadsheet) helps you spot any mistakes, and ensures that the test data you generate is independent from your code.
Better yet – if you can ask somebody else to do this for you, you’ll get an extra pair of eyes on the formula in the spreadsheet, and it will make sure your tests are completely independent.
You can also use Excel to make sure that your generated test data is formatted in a way which is really easy to copy+paste into your unit tests.
Also consider invalid inputs, exceptions and error codes. Your unit tests are there to protect your error handling and validation for “impossible” values (including null references etc.) too.
There are a couple of things you could try:
- Try to find some example values that are “obviously correct” (in the sense that any future maintenance programmer can easily see that and why they are correct, and verify their correctness in their heads without using a calculator)
- Look up the paper your algorithm comes from and see whether they have examples listed there, if yes, convert them into test cases (and put references to the paper in the documentation comment of the test method)
- Provide “obviously correct” 1:1 translations of the algorithm from the paper in one or more separate very high-level language(s) (Matlab, Mathematica, Idris, Haskell, Julia, Python, …) and write a script generating test cases using those “obviously correct” implementations. The script should be written in such a way that it can be easily verified that the generated test cases are correct.
- Try to find the simplest, closest, 1:1 translation of the algorithm (or equation) into your test language, and use that to test the actual implementation.
In the latter case, it may very well be that the implementation in the test case and the implementation in the production code end up being identical. However, that should only be a coincidence, not a goal. You should not copy&paste the implementation.
Now, why is such a test case, where the code in the test and the code in the production code are identical, beneficial? Well, quite simply: code changes. Tests usually don’t.
If there is one thing that is true about any part of any graphics pipeline, then it is that it is too slow. Now, you’re probably thinking: “my code isn’t too slow!” And you’re probably right. For your use case. For now.
But believe me: sometime, somewhere, someone will have some crazy idea. Maybe they want to use your library for real-time video processing in an eye implant powered by the bio-electric energy of the human eye, and thus with a seriously constrained power budget (and thus a very slow processor). Maybe it’s an art project and they want to render real-time video onto the entire Chinese Wall and thus need to be able to render trillions of pixels at 60Hz.
And then you will have to uglify your algorithm. Put in shortcuts. Manually unroll loops. Offload it to the GPU. Rewrite it in assembly. Maybe even implement it in hardware.
However, your unit test will still be the same straightforward, easily reviewable, easily understandable, piece of code, acting as a safety net for all the crazy, ugly things you need to do to your production code, because, well, real-life constraints happen.
In cases like these, where the unit test would essentially be duplicating the function, I’ve often wondered if I should be writing unit tests at all.
What I’ve come to conclude is that sometimes yes, it is valuable.
In most cases I wouldn’t bother with such a test, but for any critical part of a system I would because, although the test itself doesn’t seem to offer much utility, it will still alert you when another developer accidentally breaks the function.