Closed
Description
Here we can discuss more in detail what should be added there. I'll store some ideas at the same time.
First, a lot of things when it'll come to explain how to write "good tests" will be pretty generic. So I believe we might actually writte that generic version first, listing there some of the usual traps to avoid.
Then a more precise guide, python specific.
Generic guide:
Here are some ideas to use as a stump:
structure of the kata/tests suites
- always fixed and random tests (explaining why: not usual TDD practice, needed to enforce the implementation/avoid cheats).
- split the test suite using meaningful names for the different blocks
- write the assertions inside the dedicated structures (groups/blocks/methods) rather than at the base level (when it exists in the language)
- one day you'll maybe stop coming on CW, so other people will have to maintain your kata. So think about them and try to write your best code for your tests. DRY, no functions with tens of line, ...
- explain how the test framework is working (generally speaking), printing the results/data needed for the feeback right to the stdout, meaning that the user can/will print in the middle of all of this. Hence the need to:
- never do several calls to the user's function then do all the tests later. That makes the debugging a pain because the user cannot track hat assertion is related to waht call to his function
- when possible and adapted to the test framework you use to have one single call to the user's function per test method.
tests feedback
- be very carful about the assertion messages: they have to be precise and useful. "False should be True" almost always trips up newbies. General guidline: it's good to show the input, either in the assertion message, or in the name of he test (if the test framework allows to name the tests). Like: "28 is not prime. Answer should be False, but solution returned True." would be much better (hobo ; error256).
fixed/sample tests specificities:
- having only 2 fixed tests before the random tests is generally a very bad idea
- do not hide edge cases in the test cases part, provide them int the sample tests too (hobo).
- You can use all the fixed tests of the test cases part as sample tests. But if it's very long, it's not always necessary. But when you remove some of the fixed tests, try to keep at least one meaningful test for every kind of situation (hobo)
random tests specificities:
- for the random tests: prefer, when possible, to avoid to have a reference solution => design the tests so that either you know the answer right at the beginning when the input is generated if possible (hobo)
- performances oriented katas: when the random tests use inputs/outputs that are very hard to debug (strings of hundreds of characters or more, huge arrays, multidimensionnal arrays, ... anything that akes the assertion message unreadable!), split the random tests in 2 sections or more, with a first batch of small random inputs. (error256)
- the "mutation of the input" problem takes all its meaning in the random tests (give examples):
- why it's bad in general
- why it's bad on the user side (crazy feedback)
- why it's bad for the tests (cheatable)
miscellaneous
- don't rely on the random tests to enforce the requirements: all possible edge cases should be exhaustively tested in the fixed tests. As far as possible. When not possible, they should be added later if a user encounter one of them et raise an issue about it. (error256)
- This do not apply to all framework, but keeping it as a habit/default behavior might avoid a lot of troubles to anyone:
- do never compute directly the expected result with a formula in the same statement than the one calling the user's function (raising an exception reveals the solution) -> is that python specific? (I guess not...)
- all rquirements that are announced have to be tested (hobo). Like, if the user isn't supposed to use BigInt, the author has to enforce it, etc. Adding a note: "Keep in mind that when you end up with this kind of idea, you're generally about to open the pandora's box. Especially in languages that are too much flexible, like JS, pyhton or ruby. So you actually should try to avoid restrictions that may be very hard to ensure. Number of lines or length of the code, on the other hand, is not problematic (code golfing)".
general infos about the guide itself:
- "this guide is still generic, so it might not cover all cases or even not be appropriate for some languages whose framework works in a different way (QuickCheck for Haskell, for example). Please refer to the documentation of your language and/or ask for help to other experienced users." (error256)
Additional sources
- interesting ideas to gather from here (hobovsky's "kata authoring guidelines" draft)
python specific guide:
- always put the assertion methods inside a
it
block rather than at the top level or in adescribe
block, especially for the random tests. This way, when the tests are passed, the user doesn't end up with a wall ofTest Passed!
in the output panel (in addition, this allowes to see the timings for the block(s) without the need to scroll all the way down. - insist one the mutation of the input trouble, because of the order of actual and expected in the assertion methods => always compute the expected value first is the best idea to get
per language:
(or not!? B4B)
add snippet explaining how to access the solution of the user (length/chars constraint)
Feel free to dump some ideas.