Agreed with the articles, mocks are very tied to implementation details. I almost always prefer state-based testing: https://martinfowler.com/articles/mocksArentStubs.html As "observable state goes from a to b" is much closer to a business/functional requirement that will still/always be true, regardless of refactorings. Refactoring in codebases with state-based tests is a pleasure; in codebases with mock-based tests…
Dependency injection leads to elaborate, brittle fixtures that become more elaborate and arcane over time. And the sunk cost fallacy has people working for hours (and I’ve witnessed pairs spend two days, that’s over three man days!) trying to maintain them. I use mocks to arrange state A without going through all the business rules involved in creating state A. But you have to expose the states to do it, which can ma…
I concur that setting up a test for async code is a complete pain in the arse. It's extra fun when the caller doesn't get a reply, so there's no response that you can assert your as valid or invalid. It's extra, extra fun when the tests in your test suite need to run in parallel without stepping on each other.
Here's an example. Say you've got PipelineService123 that receives a message with a chunk of data, transforms that data, and sends it elsewhere. Try thinking about dynamically setting an instance of PipelineService123, wiring up an input message sender, giving that sender the dynamic address of the instance of PipelineService123, wiring up an out message receiver, making sure the instance of PipelineService123 knows how to reach the receiver, all to test whether or not everything is wired up correctly & that the data is being sent, transformed, and received properly, running this test in parallel with all of the other test cases, and keeping it reasonably easy to reason about. Good luck with that! I'm not sure even I even stayed on top of my attempt to describe it :P