Dipende dal linguaggio di programmazione, dalla disponibilità di una struttura di derisione e dalla necessità di deridere quegli oggetti complessi. Per C # e Java, sono disponibili framework che ti consentono di prendere in giro le classi senza creare prima le interfacce. (Nell'ambiente in cui lavoro, non usiamo nessuno di questi framework, quindi ogni volta che dobbiamo prendere in giro una classe per un test unitario, creeremo un'interfaccia.) In C ++, puoi evitare la necessità di interfaccia basato sul mocking iniettando la tua "classe complessa" come parametro template in ogni altro componente che lo utilizzerà (lo svantaggio è che devi templatizzare tali classi, il che significa una certa quantità di overhead).
Nelle lingue debolmente tipizzate spesso non esiste nemmeno una "interfaccia" di costruzione linguistica perché è possibile sostituire un oggetto di una classe solo con un oggetto di un tipo diverso purché la sostituzione soddisfi il contratto implicito (cioè fornisce metodi con nomi corretti e firma).
Inoltre, sono d'accordo sul fatto che DI non funzioni bene con classi di infrastruttura come "stringhe". Vedi questo precedente domanda PSE & la mia risposta ad esso .
Vorrei aggiungere che la tua domanda suona come "devo sempre fornire un'interfaccia" nel caso in cui "". IMHO è meglio seguire il principio YAGNI, iniziare senza un'interfaccia, e non appena ne hai bisogno, forse per scopi di derisione, refactoring il tuo codice e successivamente introduci l'interfaccia.