Hacker News new | ask | show | jobs
by lmm 4638 days ago
In my experience when you do this you end up with anaemic tests that are basically useless. As the page comes close to admitting, all you do is write the same code twice. The result is a lot of wasted effort on unit tests that never fail.

In an app designed like this the integration tests are the useful ones, because they're more contentful and test assumptions that are more likely to be wrong. The article flatly asserts that code written in this extremely decoupled style will require fewer integration tests, but I see no evidence for this. And you still need to figure out how to make them run fast.

3 comments

This has been my experience as well. It is far more useful to test scenarios, which generally involves some amount of integration. This also ensures the most important level of decoupling, that of the test from the system. The author's approach to TDD results in tests that are heavily coupled to implementation. This may be fine during design but they become a huge liability over the long run.

My experience is that automated testing should test the system not the implementation.

The _unit_ test for the service object might be anemic, but it's far from useless IMO. The unit test helps you design the object under test and it is written first if you are practicing TDD.

The argument for having fewer integration tests goes like this: if it's hard to write isolated unit tests for some classes you end up testing many more execution paths in integrations tests.

> The _unit_ test for the service object might be anemic, but it's far from useless IMO. The unit test helps you design the object under test and it is written first if you are practicing TDD.

But it doesn't help when your test is solving the same problem. If your test says "this object calls collaborator1 and collaborator2, then passes both results to collaborator3" then that's exactly the same design problem you'd have if you were to just write the object straight-up.

This is why people talk about BDD rather than simply TDD. It's not about having tests, it's about identifying the user-facing behaviour that you want, testing that, and then writing your objects to conform to it. This really does help your design, because it lets you start with the appropriate interface on the user side and work down from there, and the resulting object boundaries can be better than the ones you'd have thought of if you started with design. But if you restrict your test to a single class and mock its collaborators then that implies you've already decided what your class boundaries are; in effect you've already done the most important part of the design.

>The argument for having fewer integration tests goes like this: if it's hard to write isolated unit tests for some classes you end up testing many more execution paths in integrations tests.

True enough - if there are things you can't test in a unit test then you need to test them in an integration test. So if you have a complex piece of logic that you want to run several tests of, it's best if you can test that piece of logic in isolation - this is exactly what unit tests are good at.

But you still need to test all your integration pathways. All too often a class doesn't contain any logic as such, particularly when you design the way the article suggests - it's just an integration between its collaborators. For such a class there's no point in unit testing, because the only thing the class actually does is integration.

I'd argue that even a test which is a copy and paste of the implementation method - literally the same code, twice - has value, because you can then refactor the implementation as much as you like in future and know that you're covered. Nothing says that because they are the same now, they will be forever.
As someone who has personal experience making this mistake, I disagree with you. You lose the most important purpose of the test: The confidence that your code works. If you write the code twice all you know is that your code hasn't changed.

And in my personal experience, you CAN'T refactor because the test is so tightly aligned with the production code. EG: moving a variable declaration up or down one will break a test even though it didn't change anything.