Comment by shagie
5 hours ago
Trying to write the easiest code that I could test... I don't think I can without writing an excessively brittle test that would break at the slightest implementation change.
So you've got this Java:
public List<Integer> someCall() {
return IntStream.range(1,10).boxed().toList();
}
public List<Integer> filterEvens(List<Integer> ints) {
return ints.stream()
.filter(i -> i % 2 == 0)
.toList();
}
int aMethod() {
List<Integer> data = someCall();
return filterEvens(data.stream().filter(i -> i % 2 == 0).toList()).size();
}
And I can mock the class and return a spied'ed List. But now I've got to have that spied List return a spied stream that checks to see if .filter(i -> i % 2 == 0) was called. But then someone comes and writes it later as .filter(i -> i % 2 != 1) and the test breaks. Or someone adds another call to sort them first, and the test breaks.
To that end, I'd be very curious to see the test code that verifies that when aMethod() is called that the List returned by SomeCall is not filtered twice.
What's more, it's not a useful test - "not filtered twice" isn't something that is observable. It's an implementation detail that could change with a refactoring.
Writing a test that verifies that filterEvens returns a list that only contains even numbers? That's a useful test.
Writing a test that verifies that aMethod returns back the size of the even numbers that someCall produced? That's a useful test.
Writing a test that tries to enforce a particular implementation between the {} of aMethod? That's not useful and incredibly brittle (assuming that it can be written).
You are correct and the objection is just completely invalid. There's no way anyone would or should write tests like this at the client level.
I think they are just arguing for the sake of arguing.
You mention the tools you can use to make it happen.
I think we're at the point where you need concrete examples to talk about whether it's worth it or not. If you have functions that can't be called twice, then you have no other option to test details in the implementation like that.
Yeah there's a tradeoff between torturing your code to make everything about it testable and enforce certain behavior or keeping it simpler.
I have worked in multiple code bases where every function call had asserts on how many times it was called and what the args were.