Ask HN: What's your favorite software testing framework and why?
Seeing so many different testing frameworks in every programming language. Would love to understand how this happened, when they all seemingly do the same thing. Would love to know which is your favorite and what makes it different than whatever else popular framework in your language?
72 comments
[ 3.8 ms ] story [ 131 ms ] threadThere's another testing library in Ruby that is called minitest. The syntax is different, for example you would write "assert_equal num1, numb2". My problem with this is that you don't know which is the expected vs the actual values. Is it the first or second argument? On the otherhand, my framework of choice, RSpec would write this as "expect(num1).to eq(numb2)". The syntactic sugar alone makes this easier to understand.
Well, why are we using cut-down Chrome with a bunch of js scripts and pretend it's a native app or a web server?
testcontainers so I can write proper integration tests instead of mocking dependencies away.
AssertJ for fancy assertions with great failure messages.
Mockito for the rare mocking/spy usage.
Awaitility for the rare async/timing based test.
When there is a Frontend (templated or SPA, doesn't matter), I like writing the tests with Selenide assertions and using its Selenium integration to run against a large array of browsers + variants (Chrome, FF, Mobile etc) launched via testcontainers.
This stuff isn't a silver bullet, but it can go a long way to commoditising certain kinds of code change such that the dev resource necessary for them mostly disappears. At my previous place of work we had quite a few non-engineers semi-regularly contributing actual Python code. It was mostly things like adding new enum values, or tweaking properties on classes, but we had robust testing that verified all of those sorts of changes in generic ways and automatically generated tests for new pieces, and it was basically hands-off from engineering.
[0] - https://hypothesis.readthedocs.io/en/latest/
[1] - https://github.com/pytest-dev/pytest-bdd
With it you can write integration tests with typesafe YAML which double as user friendly documentation.
It also has inheritance (which gherkin doesnt) - e.g. a "buy golf clubs" story can inherit from "put golf clubs in shopping basket" story which can inherit from a "log in" story, etc.
Unfortunately, I have to say it... I hate YAML with a passion, k8s, github actions/gitlab ci, ansible, etc... When I'm doing ops jobs, I feel like I'm coding in YAML.
Btw, this hate for YAML birthed https://linkdd.github.io/tricorder/ :P
I also understand the hate from the perspective of tools which misuse YAML - putting loops, variables, conditionals, etc. in like ansible or CI tools do, like your tool complains about. I hate that too and haven't done it. I was somewhat inspired by ansible in the beginning but Ive come to understand that it was the wrong approach for configuration management and that a turing complete language is a better approach (i.e. like your rust tool).
I dont want to throw the baby out with the bathwater though. The syntax is a very clean way of displaying hierarchical data, which high level stories need. Nothing else matches - Gherkin is awful at that.
I'll look more closely into it then :)
> Gherkin is awful at that.
When I use BDD, it's because I'm handed down scenarii written by non-tech people. And it's always in the Gherkin format, then I chose the right Gherkin engine for the right tech stack. That might be cucumber, or another.
Thats interesting. You're the first person I've heard of who actually did that rather than just talking about it.
I was under the impression that this worked ok for simple scenarios but with complex scenarios I think the syntax buckles - either you end up with very long, repetitive scenarios or vague scenarios. Or even both.
Almost correct :D
The BDD scenarii also act as "user stories" in a Scrum/Agile workflow. It was a nice way to re-use what the product owner requested.
https://timothycrosley.github.io/hypothesis-auto/
http://akkartik.name/post/four-repos
https://github.com/akkartik/mu1#readme
It lets me combine HSpec for unit tests and Hedgehog for property tests in the same test files with test auto-discover. For failed properties, it prints the seed.
And it has really pretty, readable output.
I’m using Rust’s tests currently, and the output is just a lot less obvious. E.g. green text when 0 tests are run.
* The setup pattern with "just in time" variables is amazing.
* It offers some _extremely_ terse tests
* It offers a huge library of plugins.
My go-to choices per language are:
- Python: Hypothesis https://hypothesis.readthedocs.io/en/latest (also compatible with PyTest)
- Scala: ScalaCheck https://scalacheck.org (also compatible with ScalaTest)
- Javascript/Typescript: JSVerify https://jsverify.github.io
- Haskell: LazySmallCheck2012 https://github.com/UoYCS-plasma/LazySmallCheck2012/blob/mast...
- When I wrote PHP (over a decade ago) there was no decent property-based test framework, so I cobbled one together https://github.com/Warbo/php-easycheck
All of the above use the same basic setup: tests can make universally-quantified statements (e.g. "for all (x: Int), foo(x) == foo(foo(x))"), then the framework checks that statement for a bunch of different inputs.
Most property-checking frameworks generate data randomly (with more or less sophistication). The Haskell ecosystem is more interesting:
- QuickCheck was one of the first property-testing frameworks, using random genrators.
- SmallCheck came later, which enumerates data instead (e.g. testing a Float might use 0, 1, -1, 2, -2, 0.5, -0.5, etc.). That's cute, but QuickCheck tends to exercise more code paths with each input.
- LazySmallCheck builds up test data on-demand, using Haskell's pervasive laziness. Tests are run with an error as input: if they pass, we're done; if they fail, we're done; if they trigger the error, they're run again with slightly more-defined inputs. For example, if the input is supposed to be a list, we try again with the two forms of list: empty and "cons" (the arguments to cons are both errors, to begin with). This exercises even more code paths for each input.
- LazySmallCheck2012 is a more versatile "update" to LazySmallCheck; in particular, it's able to generate functions.
In terms of frameworks, I am a big fan of testify. [0] Unfortunately it doesn't seem like the testify maintainers want to incorporate generics. [1] I'm going to be releasing a library soon to address that.
I'm also going to be releasing a golang+python+typescript library for doing super cheap/fast database-backed tests. In my last job I found it incredibly useful, it essentially made it ~0 cost to write tests that exercised database-related codepaths and logic, which for most business apps is everything important.
[0] https://github.com/stretchr/testify
[1] https://github.com/stretchr/testify/issues/1147
I think it's possible to keep the implementation of testify almost (or exactly) the same, but update the type signatures of the methods to use generics, and shift-left a lot of errors to compile time. Just a nice-to-have.
https://github.com/peterldowns/testy
I think Test Anything Protocol [0] [1] is neat. Anything that takes inspiration from TAP is probably gonna be mostly structurally similar anyway.
It's easier to point out things I dislike in a testing framework. In general, anything that gets too fancy or magical, or assertion frameworks with lots of object chaining to read like natural language.
[0] https://en.wikipedia.org/wiki/Test_Anything_Protocol
[1] https://testanything.org/
https://github.com/avajs/ava/
It has a TAP reporter, but more importantly, as opposed to the more popular solutions, like Jest, the way it achieves parallelism is explained in the docs and won't change anytime soon, thus preventing wonky, hard to debug errors which occur when this part is abstracted away.
Perhaps it's an Angular thing - I don't understand why people keep pushing this combination.
Even though it's small, the extra structure and effort needed to count tests up-front is basically the same as switching to a "proper" test framework (most of which don't output TAP :( )
So to some extent the dev got unlucky, his terrible code (that he didn't test) may have been caught through the normal QA process which the QA willfully lied about doing. But that doesn't absolve him for not testing (even basic testing) his own work.
This is all inspired by the sqllite logic test framework.
[1]: https://github.com/cockroachdb/datadriven
Overview: https://eng.amperity.com/posts/2019/04/greenlight GitHub: https://github.com/amperity/greenlight
I wish the test report output was more comprehensible, and that there was a property test framework that felt more like in Haskell, e.g printing the PRNG seed when a test fails and letting you run with that seed again; it seems like proptest is the best there is, and it’s doing its own thing, kind of like how property tests work in OCaml.
https://github.com/MithrilJS/ospec
I worked with Jasmine and Jest as well, but Vitest was created recently and to me it seems to have made good improvements over both.
Also can run coverage reports that are usable in ci (I think it exports to clover format but whatever it is there is a Jenkins plugin that works with it)
https://marketplace.visualstudio.com/items?itemName=ZixuanCh...
The only real “test” is whether it works in production. Everything else is a poor substitute and gives you false confidence. It’s absurd the amount of time and effort we spend on writing tests and we still have as many, if not more bugs in our software than we did before writing tests became a religion.
Tests are like bloom filters[1], either they fail and you have a bug (either in code or in the test itself), or they pass and you possibly still have bugs.
As such they only give you false confidence if you're overly naive.
If our tests pass then we know what we know. Doesn't mean we pretend there can't be unknown unknowns, so we still need diligence before rolling out a new version into production.
[1]: https://en.wikipedia.org/wiki/Bloom_filter
(I've seen MANY examples of unproductive tests, they're definitely a very real problem - but it doesn't have to be that way.)
We've been trying to adopt Playwright but keep having issues with flaky tests.
I think my biggest pain point is just the slow page loads and servers at my company. Also depending on third party services and integrating with them makes it impossible to have a reproducible environment, I can't self-host our salesforce integration in testing environment.
Now, anything less than such functionality is second or third-rate to me. I can give older languages a pass, but anything not having this moving forward is disappointing now.
I'm looking for something for Node.js. I've used jest, but it's very heavy and does all sorts of magic and is a whole build system. Whereas I just want a test runner with utilities and reporting.
I'd love to find a JavaScript testing framework that's as pleasant to work with.
I wrote a bit about those two features here: https://simonwillison.net/2018/Jul/28/documentation-unit-tes... - about half way down.
The Smalltalk language and SUnit library had a really great signal ratio without a lot of boilerplate that goes with a lot of other xTest frameworks.
Shameless plug — I was the author of said tool.