On the other hand I know developers that only unit test. Hundreds of lines of mock code, dozens of tests. No documentation, no sample configuration for the final application and dozens of data races because the unit tests don't cover multi threading. But hey an unusable buggy mess is a net positive as long as it makes the test coverage statistic happy and that is one of those things reported to management.
While a bit exaggerated one of the downsides of unit tests is that they quickly become part of a metric without regards for when they ad value.
They tend to have much higher value than pure unit tests.
I disagree with your assertion that integration tests offer higher value than unit tests in general. Don't judge all unit tests by mock code.
But, the failures from integration tests are often a lot harder to read. Even half-good unit tests tend to show exactly where the error is, and make the fix cycle much shorter. It is also much harder to write exhaustive integration tests for all of the inputs that might happen.
I, as a long-term qa-ish person (I usually write testing systems) see real value in having a mixture of both system, integration (multiple systems), and unit tests, as well as end-to-end manual tests. Anything big enough needs all of these.
Yeah unit testing as I see it is just optimising for the success of middle management, at the expense of both the organisation's goals, and the developers.
You are making predictability the only focus, because that's the metric which middle management is evaluated by. At the expense of performance, end product quality, and work satisfaction. "It was done in exactly the excruciatingly slow and mind numbing boring way, and was exactly as shitty as designed" = success for management.
If you would try to increase performance, end product usability, and/or work satisfaction, you would have to put the predictability at risk, which means that the success of the middle manager will not be the highest priority at all cost anymore.
The push for more testing and static typing (to degree) was always managers. They trade predictability for momentum.
The reality in both situations is that bugs happen. I've not noticed a difference in frequency of "critical" bugs in either scenario. The only difference is that at the no unit test/no e2e places, getting your code into production was trivial. Which always meant when bugs happen, they are fixed much faster than the unit test-everything place. We're talking minutes vs. days here. Because at a risk-adverse business (that is, any place more than a few hundred people), you have multiple stakeholders that have to sign off if you want to go take a piss in the bathroom.
I use strategy pattern with Java functional interfaces to avoid using magic mocking frameworks. That way, my mocks are just trivial impls of the plugin parts.
Sometimes you can't really avoid it. In those cases, I would judge how much of the effort is testing your code, and how much is testing your ability to write mocks. Unless it is a really important functionality, or something complex enough that it would be easy to get wrong, I wouldn't bother with most tests that need extensive mocking. Instead handle those later with integration / e2e tests.
I've seen code so mocked it was dubious that it could be testing anything, accurately at least.
Mocking should be kept to a minimum and ideally avoided.
Mocking is most useful when adding tests to legacy code, but new code should be designed so it can be tested without the need for mocking.
But it's very easy to overdo it and end up testing irrelevant implementation details instead.
Mocking outbound HTTP calls is worthwhile, but thankfully most testing frameworks I've used (at least in Python world) make that pretty easy.
No wonder they are not getting value out of unit tests.
Yeah but the catch is that in order for your code to be "testable" you have to rewrite it completely in a way that comes with large sacrifices, making it much more confusing, abstract, larger and much harder to work with, in every way except testing it.
I totally agree that if you have to choose (and usually you do, unless you have unlimited time and funding), choose to implement end to end integration tests first.
It also helps form thinking about test cases and logic before writing code. Before I used exhaustive unit tests I would just start writing code right away and often times this would result in constant refactoring as the code evolved or during debugging. But unit tests encouraged me to scaffold out abstract functions and write tests before implementing the code. It got me thinking about what the logic and workflows then implementing the functions meant having the tests pass as verification.
Not only does it make your code more unit testable, it makes your code much more ad hoc testable. Again, I would not refactor a large, non-disposable application just to achieve this.
However, there seems to be a general skepticism around property based tests in my experience. Any idea where it stems from?
Integration tests need to be the default and people need to learn when to use one or the other.
I find integration tests paired with BDD do a good job of catching "code plain wrong" bugs as well as misused APIs and misunderstood specs. They're harder to build and run slower but the ROI is still higher.
You should know what result you want before you even start writing the code.
In theory, but IME about 50% of bugs end up being specification bugs at the high level anyway.
Once you drill down and start writing unit tests on lower levels to imitate higher level APIs that % only goes up.
If you first write the test and then try to satisfy them(test driven development) can work to an extent as it can divide the large task into small pieces where you get a prize each time but it also alienates the developer from the larger picture, diminishing their value to the project since they no longer can put their intellectual output into the project.
More green checks = more dopamine Higher quality code = long term satisfaction.
Code does things, tests verify that it does the things I thought it did. Getting a bit of validation makes me feel good while working.
If it now needs to do completely different things then it needs different tests.
Also, if you are making huge changes to tests whenever you change a single detail, that's an issue with your code, not testing in general.
Type checks are good too. Using a typed language is even better.
I'm not an all-TDD-all-the-time type by any means. You kind of have to at least have something to start writing tests against or it'll just be compiler errors all day long. But for the projects where I've most consistently found success in building correct systems, writing tests as soon as the basic interfaces are in place has resulted in more flexible, more reliable tests that last over the long term.
As for type checks...isn't that what a compiler is for?
That's literally the thesis of the Kent Beck book.
I'm not some idiot copy and pasting `expect(true).toEqual(true)`
I add functionality one test at a time. I get satisfaction of knowing it works as specified in the form of an increasing count of green checks.
I don't understand why it would alienate me.
Edit: I get that logically they are the same, but that's true of a half-empty/half-full glass. Psychologically they are different.
As you say, you don't care that there are 20k passing tests. You care just care that there is 1 failure.
If you don't believe this, just write some (good) unit tests, then make the change - it's very very likely they will catch some weird corner case you've forgotten even existed.