Nice to see this so I can convince a fellow tech lead that the reason that we’re creating interfaces for every class has nothing to do with future extensibility and has everything to do with maintaining unit tests and that even official MS sources think that’s the case (he doesn’t believe in anything until an MS based source says it).
And Unit tests make especially little sense in the specific service I’ve been talking about because all the logic is in the Stored Procs it calls. The service itself simply forwards that data.
Yep. The issue with tests that are heavy on interfaces and mocks is that they are "too close" to the code - if refactoring is changing code while tests stay green, how can you refactor when any change to any method breaks a test?
Swapping out interfaces to code-gened interceptors and keeping everything else the same, doesn't look like it would improve this underlying issue at all.
The real problem is you are testing the class behaves like the class,
but not the business-driven requirements.
Therefore refactoring becomes fearful since detecting bugs is no longer the job of tests.
Yes, depending on what you mean by integration test.
It is nuanced subject.
But say you have class A,B,C,D with mocked dependencies E,F,G,H.
There could be bugs in how A interacts with E and F that are hidden even though you have tests for A,B,C,D integrated and even unit tests on E too.
In addition, if someone comes along and refactors A,B,C,D into A',B',C',D' that have different dependencies ("Hey we are moving to microservices!") then the new integration test is different. You change test and code at the same time.
This is a problem because confidence comes from having a stable test, then changing the code and getting the green circles.
The solution (I think) is to make sure you are mocking at the level of well established boundaries. For example mock PostgreSQL. Or mock your ORM if there is a decent mock, with an in-memory version.
Then you can test end-to-end scenarios. Add a TODO, add another TODO, assert that getTodos() returns 2 results, and so on.
Instead of assert getTodos() returns 2 results when the ITodoService.getTodos() returns 2 results, and the outer getTodos just defers to it.
What is the alternative specifically for unit tests?
You can create integration and e2e tests that aren't as sensitive but I don't really understand what people are suggesting when they say mocking is bad in unit tests.
It is irrelevant and driven by some testing evangelists
Tests are either quick or slow and may touch external stuff, thats mostly it.
Whats wrong with mocks? If they lead to scenarios where your tests are green, but app doesnt work then it sucks. Ive witnessed projects with all green tests but app wasnt even waking.
I think there is an evolution developers go through. Right around the "design pattern" stage where everything is decoupled, hinges for all parts. Devs seem to focus on the test coverage value. Both these attributes stem from the same failure to understand the underlying business. The model rarely reflects the actual business needs (usually because the dev spends more time reading about, and thinking about tech as opposed to understanding the business) they use flexibility in the codebase as a fallback because gee the business can be anything. Code coverage equally fails because they only think about their code. Sometimes the most useful thing is a business case that was never thought about. It's code that doesn't exist. Perfect code coverage won't tell you anything about code you haven't written.
I'd rather spend time trying to learn the business, and where it's going, and why my product is useful, than mocking out some dumb part I can test a few times manually. Unit tests are super valuable tool, but not everything needs a unit test.
I hate it as well, and seldom do it on my own projects.
At work, I have better things to do than waste time on this for PR, so I create those interfaces, thankfully there are VS tools to create them automatically.
They're not required for MS's dependency injection, not sure about other libraries. 95% of the interfaces I've ever seen used for DI only have one implementation, and that 5% is generous. This makes maintaining the class and the interface tedious.
The reason this is so common is probably because code examples (including MS) often have it, so it gets followed the first time and repeated.
Absolutely my experience is well, I have been writing .NET for over a decade, and I think I have seen a handful of cases mainly when we were bored or when I remembered the Interface Segregation principle and tried to shoehorn it into code (though the principle does work well to have methods take the minimum number of props needed to do their job, but that's a whole other topic).
And Unit tests make especially little sense in the specific service I’ve been talking about because all the logic is in the Stored Procs it calls. The service itself simply forwards that data.