I agree. Arguments against the classical test pyramid are common, but IME they often come from a place of laziness. It essentially boils down to "unit tests are a chore to maintain, hence ...<hand waving>... integration tests are more valuable". Laziness _is_ one of the three virtues of a good programmer, but these arguments completely miss the mark. I would actually go a step further and argue that their popularity is doing tangible harm to software quality.
For a program to be robust it needs to be tested at all layers, and the testing pyramid is still a good rule of thumb to follow. Designing the program with testing in mind is crucial, and as you say, interfaces and their equivalent in any language are the right approach to achieve this. There was a period of time when monkeypatching was popular in dynamically typed languages, and perhaps still is (I've been out of the loop for a few years), but we always knew that it was a dirty trick, and that the software should be architected better to make it testable.
If anyone is curious what this might look like, I wrote a small project in Go[1] last year that is built from the ground up with testing in mind. There's a global `App` struct that is initialized in `main() `, and serves as the main container for all I/O the program does. This means that internally no component ever makes direct OS calls, but does it via interfaces, which can be easily mocked in tests. This makes unit and integration testing a breeze, and allows other niceties like passing down a global logger. I've adopted this pattern in all Go apps I write from scratch, and advocated for it on existing projects I've worked on, and it's worked great so far.
[1]: https://github.com/hackfixme/disco