DI torna o teste de unidade muito mais fácil. Mas você ainda pode escrever testes de unidade sem DI. Muitos testes de unidade já foram escritos antes que a DI se generalizasse. (É claro que algumas dessas técnicas usadas são idênticas ou muito parecidas com DI, sem saber que tem um nome chique: -)
Eu mesmo usei, e. interfaces e fábricas muito antes de aprender sobre DI. O nome da classe de fábrica real pode ter sido lido de um arquivo de configuração ou passado para o SUT como um argumento.
Outra abordagem é usar singletons (ou dados globalmente acessíveis em geral). Sim, eu sei que não é recomendado por muitos (inclusive eu) em geral. Ainda assim, pode ser viável em situações específicas, especialmente se o singleton contiver dados de configuração estática que não sejam específicos do caso de teste, mas que difiram entre a produção e o ambiente de teste. Claro que tem seus problemas conhecidos, então DI é superior se você puder usá-lo. Mas muitas vezes (por exemplo, em sistemas legados) você não pode.
Falando nisso, Trabalhando efetivamente com o código legado descreve vários truques para obter o código legado coberto pelos testes . Muitos deles não são bons e não são uma solução a longo prazo. Mas eles permitem que você crie os primeiros testes unitários valiosos para um sistema que, de outra forma, não seria testado ... o que permite que você inicie a refatoração e eventualmente (entre outros) introduza o DI.