Hacker News new | ask | show | jobs
by BlackFly 1253 days ago
Unit tests (class or method as the unit) hinder refactoring by binding to low level implementation details. When you refactor, by definition you are changing what the factors (units) are. Generally, your unit tests will then be testing implementation details that no longer exist. By strongly coypling to implementation details, unit test suites suffer an extremely large ripple effect on refactoring.

Tests in general can only help you refactor code at a lower level of granularity than what you are testing. Something lower than unit level is a contradiction.

Of course, you can instead test business behavior which isn't as volatile in refactoring and change your definition of unit to be a unit of practical business requirements...

1 comments

If a unit test covers function f(a,b) to ensure it always returns the right answer in the domain of a,b

But a developer looks at f(a,b) and realises a new implementation could be 10x faster.

The developer re-writes the function, the tests still pass. Without that test they couldn't be sure their rewrite didn't break the expected behaviour.

What you're talking about is changing interfaces and structure when refactoring. Yes, unit tests can make that more painful. But you bin your old tests and write new ones.

If your unit tests are not cheap to dispose of, run or rewrite ... that's your core problem.

If your unit tests are not testing discrete units, instead testing the combined behaviour of many units (the re-factoring of structure pain) ... then you have bigger issues than the tests; namely understanding the difference and applicability between unit and integration tests.

> If your unit tests are not testing discrete units, instead testing the combined behaviour of many units

This is a very naive idea of unit tests. In the real world applications have a dependency hierarchy with more than two levels. If you want to test anything other than the leaves in that hierarchy you are by definition testing the behaviour of many units.

Sounds like what you're saying is that unit tests are only applicable to leaves in the dependency hierarchy. I could agree with that, but that's not what the world describes as unit tests. That would also make unit tests quite useless.

> Thay would also make unit tests quite useless

Not so. Their use is to aid refactoring of those nodes. Also the leaf node can contain calls to other code (leafish) if that code too is unit tested.

They're the sum of their parts.

Integration tests used lightly to test specific integration occurs (ie f() not only meets X constraint but calls y() in a certain way to do so) if that's important.

The fact that most codebases unit tests are a fragile mess, and that many devs find tests a burden highlights just how poorly the topic is understood.

> Their use is to aid refactoring of those nodes

Sure. But there are codebases where leaves are less than 20% of the code. Yet testing preachers are saying that unit tests are the most important part (e.g. Martin Fowler's Testing Pyramid). Though again, they use a different definition of unit tests: everything is a unit if you mock enough.

> how poorly the topic is understood

Well, at least one of the reasons is that people focus too much on test classification. You need to decide what is important to test, how is just implementation detail. If that's your approach, you'll realize that you're getting the most value from integration tests. The only downside to this is losing Testing Church membership. But that happens when you apply logic and reason.

Instead, everybody decides that "we write unit tests", without thinking what needs to be tested and recognizing limitations of unit tests.