-2

The second and last question in my current series of questions about TDD and unit tests...

For the purpose of this question, a 'unit test' is defined as a test that focuses only on a particular method and that mocks all dependencies of the class of this method. (As opposed to any other sort of automatic test, such as an integration test or any test that does not mock dependencies). TDD is defined as a cycle when I write a unit test, the smallest test that is necessary to write for it to fail, then I write some production code, again the smallest bit that is necessary for the test to pass, then I refactor.

I've always dreaded unit tests and TDD. The reason is that I tried this methodology once in my life, and my productivity, as a result, nosedived to almost zero.

I then learned there was also another approach to testing, one that preferred tests at a higher level of integration and avoided mocks. I thought this was the answer. I thought that unit testing and TDD were hostile to productivity because of the tremendous overhead they incurred; also they forced an architecture that was proliferating with lots and lots of tiny classes, functions, abstractions etc, again incurring overhead.

It appears that, to my great fear, I am now actually forced to write unit tests. I am told I must cover all my code by unit tests that mock dependencies. Mocking dependencies is required. Rigorous TDD is expected to be enforced at an unspecified future.

To be frank: I fear that I will soon become unable to write any code, because of the tremendous overhead of adhering to TDD and requirement to cover all code I write by unit tests (with an emphasis on unit).

How do I write lots and lots and lots of unit tests while remaining productive?

gaazkam
  • 4,417
  • Why do you think that writing unit tests is not being productive? – mmathis Mar 15 '24 at 17:06
  • @mmathis not productive in the sense that doing so takes me many times more time than writing code without them... mocking all classes takes time, writing a test for each function takes time... – gaazkam Mar 15 '24 at 17:11
  • 1
  • @mmathis second thing is, and I asked it in the other question, but it got downvoted... unit tests are supposed to catch bugs, but how do they catch bugs? they wont catch a bug in the piece of code that is being tested, because if that piece of code changes, so must the test - so the test got unused at the very moment it could prove worthy. They wont catch bugs in any other piece of code because it is mocked. So, writing such unit tests seems to me to be a wasted effort. – gaazkam Mar 15 '24 at 17:40
  • @GregBurghardt see above. – gaazkam Mar 15 '24 at 17:40
  • @gaazkam your unit tests are only as good as your test data. If your test data is wrong, your unit tests won't catch (those) bugs. They won't catch bugs in the mocked dependencies, but the tests for those mocked classes will ;) – mmathis Mar 15 '24 at 17:54
  • @gaazkam when writing your tests, if you expect the result from some method call to be X, but it comes back Y, you need to figure out why there's a difference. Is it in your test data? Or is it your method? Or are both wrong? If the problem was in your method, your test just caught a bug – mmathis Mar 15 '24 at 17:56
  • 3
    Read through What is the point of unit tests?, especially the comment thread. To quote Kilian Foth in the comments: "you're hitting your head bang-on preparedness paradox." – Greg Burghardt Mar 15 '24 at 17:56
  • 1
    @GregBurghardt I do not doubt the need of automated tests. I do, however, doubt the emphasis on unit tests. In my mindset, we should start from integration tests, then fall to the level of unit tests when we must test something that is imparctical to test by integration tests. In all cases, we should strive to avoid mocks. For me, unit tests mainly test the way the code is implemented, while integration tests test how the code behaves, and the latter needs to be tested, not the former. Guarding against code misbehavior facilitates fearless refactoring, testing implemenation solidifies code. – gaazkam Mar 15 '24 at 18:09
  • However, I am forced to work under the opposite principle, namely to first and foremost have 100% coverage by unit tests that mock everything, then rise to the level of integration tests if needed. – gaazkam Mar 15 '24 at 18:09
  • Maybe I will be convinced by practice? Maybe, contrary to my expectations, I will, indeed, see unit tests (with an emphasis on unit) facilitate fearless refactoring and ensure correctness instead of incurring tremendous overhead while solidifying the code? – gaazkam Mar 15 '24 at 18:15
  • 3
    "unit tests mainly test the way the code is implemented" You are writing bad unit tests then. – Philip Kendall Mar 15 '24 at 18:31
  • @gaazkam I wonder whether the resources you've seen about unit testing have fallen into the trap of describing a "unit" as a "unit of code" such as a class or function. Such books, blogs and even stackoverflow answers which perpetuate this falsehood are sadly everywhere, and unfortunately have led to large swathes of developers churning out tests which merely test the structure of code rather than its behaviour (Kent Beck, the author of the original "TDD" book from ~2001 has frequently called this out, although such practices are still unfortunately too widespread). – Ben Cottrell Mar 15 '24 at 19:35
  • 1
    At risk of saying something you may already know, a unit is a unit of behaviour that relates back to some functional requirement (which has meaning and value to a user or stakeholder) and not a 'unit of code'. Indeed, the size/breadth of the code covered by a unit test is irrelevant to the concept of a unit; When code happens to be poorly structured, a unit of behaviour may happen to run through dozens of classes and functions, although in an ideal world, code would be organised along the lines of behaviour, but this is not always the case, especially in large legacy codebases. – Ben Cottrell Mar 15 '24 at 19:37
  • @BenCottrell My current problem is that 100% coverage of any new code by unit tests is a new policy at my workplace. I am, to my dread, forced to write tests with mocks. When I wrote a simple microservice that has little logic and mostly just integrates with other microservices, my first intuition was to cover it by integration tests first, and avoid mocks; however, I am forced to, instead, cover it by per-class unit tests that mock dependencies. – gaazkam Mar 15 '24 at 19:40
  • Honestly I'm not sure if I'm misunderstanding requirements at my workplace, or if I misunderstand you, or I we think similarly, or if I'm too blinded by my own views to see any other point that might contradict my preconceived beliefs. – gaazkam Mar 15 '24 at 19:43
  • @gaazkam I suspect that the integration tests that you're thinking of writing would probably serve perfectly well as unit tests and get you very close to 100% coverage. The I/O libraries at the boundaries might affect how easy/difficult this is, but hopefully any boilerplate for mocking something like an HTTP client/connection or API would be something widely reusable for a lot of tests. If you're using some widely-used framework then I wouldn't be surprised if someone has already published ready-made mocks that you can just install and wire up with your own mock data. – Ben Cottrell Mar 15 '24 at 20:16

0 Answers0