The Design and Use of QuickCheck
begriffs.com
begriffs.com
https://hackage.haskell.org/package/base-4.9.1.0/docs/Text-P...
Property testing is a good idea in any language, but its particularly elegant in Haskell.
I've wanted to use it for my current React-Native project. I found a JS implementation AND a babel plugin that creates the tests from flow-types.
The question for me here is, how long does this take compared to regular testing? I read people complained about AVA because of bad test performance (it does many parallel tests).
Also, where to put it in the chain?
Should I put it pre-push, pre-build, somewhere in the CI?
But it isn't the answer to everything- sometimes you really want to test just one case.
object Generators {
val commentsGen: Gen[Comment] = ???
val userWithTwoCommentsGen: Gen[User] = Gen.listOfN(2)(commentsGen).map(comments => new User(comments))
val userWithNoCommentsGen: Gen[User] = Gen.const(new User(List.empty)
val userGen: Gen[User] = Gen.listOf(comments).map(comments => new User(comments))
}
And then your test code can get right to the point so to speak "Users with comments" should "karma equal to the sum of their posts" in {
val users = Generators.userGen.take(10).foreach(u =>
u.karma should be (u.comments.map(_.points).sum)
)
}I never thought about the connection of that with Quickcheck
Of course, with randomised testing, there's an inherent non-reproducibility, so maybe this isn't as unfortunate as it sounds?
Interesting - I understood that Git stores whole files, not diffs, so I'm surprised this is a significant feature.
According to our docs (http://hypothesis.readthedocs.io/en/latest/database.html?hig...), you can check the examples DB into a VCS and it handles merges, deletes, etc. I don't know anybody who actually does this, and I've never looked at the code for handling the examples database, so I have no idea how (well) this works.
If tests do throw up a particularly interesting and unusual example, we recommend explicitly adding it to the tests with an `@example` decorator, which causes us to retest that value every time. Easier to find on a code read, and won't be lost if the database goes away.
(Disclaimer: I'm a Hypothesis maintainer)
1. Unambiguous specifications [vs human language] of exactly what the function expects, should maintain, and should output. This is basis of Design-by-Contract method of verification in Eiffel (original), Ada, and SPARK.
2. Ability to check inconsistencies on that if a formal, specification tool. An example is where unique types are defined for miles and kilometers that require a conversion before used in same-sized variable. Might have prevented a rocket from being destroyed.
3. Ability to formally prove through automated or interactive means that your code has those properties in all cases rather than whatever you tested for or your fuzzer came up with. Frama-C, Java w/ JML, and SPARK Ada can do this with SPARK the champ so far. It's hard and limits how you express the problem or what problems it can handle but with highest payoff in correctness. Anything unproven can be handled by a runtime check the tool might even insert for you.
4. Academic and commercial tools exist that can automatically generate tests from those specs. So, you get tests covering whatever you can specify automatically with no human effort. You can use your brain on stuff the tools can't handle.
5. Ability to do equivalence checks more easily on annotated (i.e. constrained) programs can help show optimizations or compilations didn't break your program.
6. The annotations might also be used for synthesizing programs or optimizing them even more than usual.
So, these are some benefits that come from leaning toward formal specs of code or within code instead of just manual, unit testing.
All determinism guarantees is that you'll either reliably detect a bug or reliably not detect a bug, and hence get a nice aesthetic line of green checkmarks followed by red crosses in your test history. Or just get a nice looking line of green and not even know the bug is there. This seems like a small thing to sacrifice to detect more bugs.
Of course, regular unit tests have their place too, when your specification can't really be expressed as a "forall" property.
Truth is, there's no need for an either/or holy war about this. The two styles are easily mixable: property based testing for broad coverage, specific runs for specific coverage.
The situation you want to avoid is this:
Run tests, find bug.
Try and fix bug.
Rerun tests, tests pass.
Deploy fixes to production.
Later find out that the bug is not fixed, your second run of tests simply didn't hit the right combination.
Rather, to me, the issue is exhaustive testing vs. non-exhaustive testing. Typically, critical issues in complex systems only arise extremely rarely. Consequently, randomized testing typically does not bring them to light. There may definitely be cases where it is useful, and it can certainly easily be more exhaustive than a small hand-picked set of unit tests. But the core issue is still that I typically need exhaustive tests to be more certain about relevant properties.
The rule seems to be that about 1/3 of the problems it finds are legitimate bugs in the codebase, 1/3 are problems with the property (e.g. stating something a bit too strong), and 1/3 are problems with the data generators (e.g. not satisfying some invariant).
The latter 2/3 can either be seen as overhead, or as fixing bugs in one's understanding of the codebase. Usually it's somewhere in between.