4 ms·
What, no. That's exactly the opposite. Unit tests protect you against current mistakes. They're tied to the exact implementation. "Right now my function X sho
by inertiatic 6y ago
What, no. That's exactly the opposite.
Unit tests protect you against current mistakes. They're tied to the exact implementation.
"Right now my function X should call Y on it's dependency Z before it calls A on it's dependency B.
I know that my method should do this, because this is how I designed it now.
Let me write a test and expect exactly that."
Integration and unit tests will tell you whether in the future your code will still work when you refactor.
"Okay, we rewrote the whole class containing the function. Does running my thing still end up writing ABC into that output file?"
Otherwise I agree with you mostly.
- notacoward 6y ago> They're tied to the exact implementation. If unit tests are tied to an exact implementation, they''ll fail on correct behavior and that's definitely wrong. It shouldn't matter whether X calls Z:Y or B:A first, whether it calls them at all, whether it calls them multiple times, whether it calls them differently. All that matters is that it gets the correct answer and/or has the same final effect. Unit tests should be based on a module's contract, not its implementation. This is in fact exactly what's wrong with most unit tests, that they over-specify what code (and all of its transitive dependencies) must do to pass, while by their nature leaving real problems at module interfaces out of scope.
- inertiatic 6y agoa) Most code in the wild doesn't have an explicit output and instead is orchestration code. b) Even if you have an output, it's dependent on more complex input of arbitrary types. Assume that there's a method that returns an input based on summing the output of a method call of it's abstract dependencies. To do dogmatically correct unit testing you'd pass those 2 mocked dependencies, and have those methods return the values when the right method is called on them. Then you'd assert that B was called on A, that D was called on C, and that the method under test returns the sum of those returns. As soon as you move into passing implementations of those 2 dependencies, to anyone dogmatic you're doing integration testing. Even if the tester isn't being dogmatic, in a lot of cases these inputs are complex enough that building enough actual inputs that are consistent and realistic to cover all the cases is prohibitively costly, so they opt for mocks. Now, suddenly you just have more code to maintain when making changes, but you feel good about yourself.
- kingdomcome50 6y agoThe interface on our object (O) that you are describing is: O -> int Your unit test is concerned with narrowing the interface above to: O -> int // of specific value based on dependencies If Os only dependencies are A and C, this can be rewritten to: A -> C -> int // of specific value Of course if we assume both A and C, themselves, have dependencies we can recursively rewrite the above until we have a very long interface, but instead you have opted to mock (M) them: M(A) -> M(C) -> int // of specific value You then take it a step further and mock the method calls on each to return a specific value: M(A) -> int M(B) -> int becomes: M(A) -> 3 M(B) -> 5 Okay. Now we can rewrite our interface to: 3 -> 5 -> int // of specific number and our test to: 3 -> 5 -> 8 and make our assertion that the result is indeed the sum of the inputs (not to mention the ridiculous assertions that specific methods were called within the implementation). Yikes... No wonder OOP gets a bad wrap. All that for what amounts to a `sum` function. The designer of the above monstrosity could learn a lot from the phrase "imperative shell, functional core". It sounds like dogma until you are knee deep in trying to test the middle of a large object graph!
- tremon 6y agoNo, unit tests aren't tied to the current implementation, they're tied to the current interface. If your programming interface calls for multiple interdependent objects without central coordination, then yes, you should test that. But I would say that you've already started out with code that is too badly structured to allow for testing the units in isolation: you should be able to unit test A without relying on Z at all. It's integration testing that validates that all your units still combine (integrate) into a working end product. That's not about testing your implementation nor your internal interfaces, that's about testing your program's inputs and outputs.