Live data from Hacker News

Slow Tests Are the Symptom, Not the Cause

re-factor.com

11–20 of 46 posts

Re: Slow Tests Are the Symptom, Not the Cause

#11
post #4

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 ass…

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.

Re: Slow Tests Are the Symptom, Not the Cause

#12
post #2

Was mostly with the author right up until he shows an example of the worst possible unit test. Testing the implementation and not the intent. This feels like how Spring must have been born. Trying to think of an alternative, and I think I would be happy with the fat controller example originally given. It's simple enough and straight forward. Perhaps it's just bad examples all the way around...

I'd be happier with the fat model version than the fat controller. At least that way is amenable to Using the same code via multiple different user interfaces. For example, the normal web site, a mobile app, a data import, and an admin path, and something else that hasn't been though of yet. In general, designing things as though there will be more user interfaces for it in the future is helpful, and tends to push logic out of the controllers.

I see a lot of unit tests like the ones described in this article and have some doubts about their effectiveness for doing much more than spotting syntax errors. Integration testing is still important if the goal is avoiding defects getting out to production.

Re: Slow Tests Are the Symptom, Not the Cause

#13
I don't know (and don't care) if this makes your unit tests faster, you shouldn't write code like that. He took something that should be simple and made it really complicated. The execution time of your tests shouldn't dictate how you will design and write your application. Especially if it makes things harder to maintain and understand.

Edit: I love tests and they should be fast so that you can run them every time before pushing your changes but as I said they shouldn't dictate how you design and write your application.

Re: Slow Tests Are the Symptom, Not the Cause

#14
Pluggable systems (loose coupling) are the easiest way to create a system that is cheap and easy to maintain. I've found that a lot of Rails developers in particular don't see the benefits of this and they really, really like tightly coupled systems because of the convenience of it all.

Benefits like fast, easy testing, easier maintenance, swappable external things like databases, queues, external api's are apparently not enough of a benefit to change away from rails.

I wrote a pattern (http://obvious.retromocha.com) that makes pluggable systems easy based off of ideas from Uncle Bob, Alistair Cockburn, and Kent Beck which I think makes writing pluggable systems a lot easier, but as with all similar structures like Hexagonal Architecture and the like, Rails devs seem to not see the value in it until their app is a complete mess, at which point they would rather add more rails code than fix the underlying problems.

For a lot of developers, now that rails is popular, rails IS the architecture and things that go off the rails are either a waste of energy or too difficult.

Rails is what Kent Beck calls a connected architecture and as his incredibly scientific graph describes, the cost of change spikes dramatically over time compared to a modular architecture: http://www.threeriversinstitute.org/blog/wp-content/uploads/...

Until developers adopt modular or at least more service oriented designs, nobody should be surprised by slow tests, features taking too long to ship, the cost to ship code to rise dramatically over time. It's mostly our own fault.

Re: Slow Tests Are the Symptom, Not the Cause

#15
post #4

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 ass…

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.

Re: Slow Tests Are the Symptom, Not the Cause

#16
post #13

I don't know (and don't care) if this makes your unit tests faster, you shouldn't write code like that. He took something that should be simple and made it really complicated. The execution time of your tests shouldn't dictate how you will design and write your application. Especially if it makes things harder to maintain and understand. Edit: I love tests and they should be fast so that you can run them every time b…

The point here is not that the tests execution time should dictate the design. On the contrary, the idea of this post was to stress the importance of good design, and that fast tests are just a side effect of it. Now, you might disagree with me on what constitutes good design but that's a different issue.

Re: Slow Tests Are the Symptom, Not the Cause

#17

Pluggable systems (loose coupling) are the easiest way to create a system that is cheap and easy to maintain. I've found that a lot of Rails developers in particular don't see the benefits of this and they really, really like tightly coupled systems because of the convenience of it all. Benefits like fast, easy testing, easier maintenance, swappable external things like databases, queues, external api's are apparentl…

Where I work, we just had a long internal debate about the usefulness of monolithic frameworks such as Rails and Symfony. I argued against the frameworks, but much of the team is committed to using them. The convenience of having everything in one place apparently is important to a lot of people, despite all the problems that are caused by that. I posted the debate here:

http://www.smashcompany.com/technology/an-architecture-of-sm...

Re: Slow Tests Are the Symptom, Not the Cause

#18
post #15
post #4

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 ass…

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.

Re: Slow Tests Are the Symptom, Not the Cause

#20

Pluggable systems (loose coupling) are the easiest way to create a system that is cheap and easy to maintain. I've found that a lot of Rails developers in particular don't see the benefits of this and they really, really like tightly coupled systems because of the convenience of it all. Benefits like fast, easy testing, easier maintenance, swappable external things like databases, queues, external api's are apparentl…

Here's Uncle Bob talking on his version of the idea extensively: https://www.youtube.com/watch?v=WpkDN78P884 The main idea is to separate the system into entities, interactors and boundaries, which correspond roughly to model, controllers and interfaces+DTOs (Data Transfer Objects), but with the difference that model classes are not exposed through the interfaces. Rather, the DTOs are used to communicate the changes in the data.

We have been using this approach for a year now, with REST on top, and it so far has worked beautifully. The entities map naturally to REST resources and DTOs makes nice request and response classes.

Armed with CQRS (now I sound enterprise-y), the data storage is easy to work with using the same DTOs (the data storage and its interface do not know anything about the entities either). On the DB implementation level, we work on the DTOs, which are trivial to store to the database (even without ORM, although it does remove much of the boilerplate). Only one class - the entity repository - is needed to convert the DTOs to entities, and this only happens on create, update, and delete. Reads (due to CQRS) go directly to DB without instantiating any entities - neat. We also use 'unit of work' -pattern to abstract the notion of transaction on create, update, and delete operations, which simplifies the interactor code, hiding the nature of the data storage (making it a swappable in this respect too).

But to stay on topic, our tests are fast too as the interactors can be tested by injecting an in-memory database as a data storage to test the business logic, and the interactors have no dependencies on any frameworks, web or otherwise. Testing the interfaces makes BDD feel natural and also supports clear separation of the REST layer (the communication channel) and the interactors (business logic).

Post reply on HN