5 ms·
I pretty much agree with all of this. Unit tests are great when you have a large code base with dozens of apps and need to modify a core library to remove side
by snarfy 9y ago
I pretty much agree with all of this.
Unit tests are great when you have a large code base with dozens of apps and need to modify a core library to remove side effects. How do you know you didn't break one of the apps?
But if we go deeper, why does the core library have side effects? Because it was a crummy, poorly designed piece of crap to begin with. If it was originally written with high quality it wouldn't need the refactor now. The author noticed this. He wrote good code to begin with so it didn't need a lot of testing.
When you have a large team of mediocre engineers you need unit tests to guard against more bad code from getting in. They might even be brilliant engineers, stuck in a horrible process of churning out features to unrealistic deadlines.
If you have a great team, a great process, and a great budget with realistic goals, you can crank out amazingly good software without the need for a lot of unit tests. But that's not the real world. In the real world we have lots of unit tests. They are a band-aid over the other problems without addressing them specifically.
- josteink 9y ago> I pretty much agree with all of this. It's easy to disagree with someone who portrays an enemy which doesn't exist. I disagree with this piece everywhere it manages to land somewhere concrete, where its claims can be verified and assessed. > But if we go deeper, why does the core library have side effects Let's not get ahead of ourselves in theoretical, non-applied functional programming. As even Haskellers recognize, all computing would be meaningless if there ultimately was no side effects. In fact, we use computers for their "side-effects". Sometimes you need side-effects. And sometimes you unit-tests are great way of automatically verifying that you have the right side-effects.
- lmm 9y agoSide effects are by definition outside the thing itself, so they're not really amenable to unit testing; you have to use integration tests.
- bonesss 9y agoJust... small point, but: side effects can be encapsulated, abstracted, and injected to create testable code in "side-effect" heavy situations. Complex running dialog with a hardware serial device, for example, could be rearchitected as a component that has a clear-text dialog with a serial device proxy. This would create functional, side-effect free, testable units of code while also maintaining rich side effects in production (handled by pure integration tests). Essentially: if you _have_ to use an integration test you should almost always be refactoring into something that you handle with unit tests in conjunction with your integration tests. Otherwise you're leaving maintenance developers with too much risk when making changes and no clear separation of your domain model from your application code.
- lmm 9y ago> side effects can be encapsulated, abstracted, and injected to create testable code in "side-effect" heavy situations. Absolutely, but at that point you're moving the side effect out to the boundary and separating it from the logic. Then you can unit-test the logic away from the side effect (but the test of the side effect itself still has to be an integration test).
- BlackFly 9y agoclass AccessCountedInteger { private final int value; private int accessCount = 0; AccessCountedInteger(int value) { this.value = value; } public int getValue() { accessCount++; return value; } public int getAccessCount() { return accessCount; } } This unit has side effects on the method getValue() that impact what is returned by getAccessCount(). While it is contrived, if you are making something following the builder pattern, you will in general have a lot of side effects from all of the methods called on the final build() method. This is quite amenable to unit testing.
- lmm 9y agoThat is not the kind of side effect that Haskellers would acknowledge as necessary (and I'd see it as a bad idea). We can call it a "side effect" but I think there's a qualitative difference between that and something like file I/O or async.
- tome 9y ago> In fact, we use computers for their "side-effects". We use computers for their effects. "Side-effects" are things that happen by accident, or incidentally, when we were trying to do something else.
- oblio 9y agoI think his point is that from the functional perspective I/O is a side effect, since you're mutating state. But for almost normal beings the I/O is the thing that actually matters, not the twiddling of bits in computer memory.
- tome 9y agoQuite the opposite. In Haskell I/O is an explicit effect. In impure languages I/O is something that happens as a side effect of just calling a function.
- nradov 9y agoEven the best code written by the best developers will eventually need to be refactored if the software lives long enough. Changes in requirements, environments, and tools will eventually invalidate the initial design. If the software will only be used for a couple years and then discarded then you can get away with minimal unit tests. But extensive unit tests are absolutely essential to successfully maintaining a system with a 10+ year lifecycle.
- mempko 9y agoThe kind of refactor that you are talking about will require a rewrite of the unit tests. Unit Tests are a burden in restoring. They only stay the same if liskov substitution holds. Which is rare in any significant change.