4 ms·
I agree that it may be more useful to test a larger part of the code together instead of trying to do everything at the finest levels of detail. However, my poi
by Chris_Newton 5y ago
I agree that it may be more useful to test a larger part of the code together instead of trying to do everything at the finest levels of detail. However, my point here is about more than just the level(s) where you perform the tests. It’s also about what kinds of testing you are doing. Here are some of the most common possibilities:
• xUnit-style tests for specific cases
• Property-based testing
• Snapshot-based testing
• Manual testing
• Formal verification
• Peer code review
All of these can be useful under the right circumstances.
If your code produces output that is best checked by human inspection but shouldn’t then change (or change very much) then snapshots may be a good choice.
If your code won’t deterministically produce the same correct output every run but whatever output it does produce should always satisfy certain conditions, maybe property-based testing is a good way to go.
If your code involves communication with external equipment that requires operator interaction to do anything interesting, fully automated testing might simply not be possible. In that case, manual integration testing with someone physically operating the equipment might be appropriate.
Formal verification covers a wide range of possibilities from the likes of basic static type checking all the way up to the use of automated theorem provers in specialised programming languages. It almost always has some extra cost in terms of annotating the code but it can sometimes produce far more powerful evidence of correctness in the general case than any test suite checking individual cases ever could.
Code reviews are pretty much universally good, as long as you’ve got enough people available with the relevant knowledge to do them.
Sometimes, these testing techniques are complementary and using more than one of them together might be beneficial. At other times, a coding style or process that favours one might make another more difficult.
This HN discussion is mainly about the style of “testable” code that TDD tends to produce, with many small units and lots of dependency injection, which is of course very friendly to small-scale unit testing. However, it might also be more difficult to review because of all the configurability and indirection. If it relies on doubles to stand in for external resources, it can end up testing the accuracy of the simulation more than anything else, so adding very little (justified) confidence that the real system is operating correctly. And as a rule of thumb, individual testing of specific cases may be less effective than testing large numbers of generated cases with property testing, which in turn may be less effective than proving that all cases work via rigorous analysis.
And this brings me back to where I came in, which was that prioritising “testable” code in the TDD sense isn’t necessarily a good thing. The real meaning of “testable” depends greatly on what type of code you’re writing and which testing strategies are most helpful.