The goal of the tests is to answer the question "if the pipeline is green, does it give us confidence it will work exactly as intended in production?". Mocks work against this goal.
Especially nowadays when writing tests is dirt cheap because LLMs can often get them right at the first try, there is little reason to avoid approaching this problem without prioritizing sanity over following stupid cargo cult that should have died a decade ago.
The Go approach is a breath of fresh air when it comes to mocking and testing.
You cannot eliminate complexity (although you can minimize it, to be clear). But for most things, you can shuffle the semantics around, you can paper over them in a new way, but at the end of the day, someone has to pay the piper.
In my experience, complexity eliminated from one spot will invariably bubble up somewhere else in an unexpected way.
It's still possible, mind you. TypeMock has been offering this exact ability for C# for many years now. But the free TDD frameworks generally didn't have this.
This is not necessarily zero-cost however - if the compiler cannot prove specific type members being invoked, it has to construct an execution profile and then apply it to subsequent compilations, and also emit a guard when doing dispatch on those.
[0]: https://github.com/dotnet/runtime/blob/main/docs/design/core... (note - subject to change)