A injeção de dependência não empurra a carga de teste ainda mais para a cadeia?

9

Estou aprendendo sobre injeção de dependência e, embora eu possa ver o apelo disso ao escrever bibliotecas funcionais, não vejo como isso resolve alguma coisa quando você também é quem usa as bibliotecas.

Isso torna o teste da biblioteca muito mais simples, porque não há muito o que testar.

Mas, eventualmente, você terá que testar sua função injetada ao usar a biblioteca e lidar com as funções de zombaria e stub da biblioteca padrão.

Este é um caso concreto com o qual estou lidando no Node.js :

function compile(options) {
  var files = options.files;
  var texCompiler = options.texCompiler;
  var pdfMerger = options.pdfMerger;

  return Promise.all(files.map(texCompiler(files)))
    .then(pdfMerger);
}

Isso é trivial para testar: injetar objetos simulados ou espiões como o texCompilere pdfMergeré um pedaço de bolo, porque a função realmente não faz muito. Tudo o que posso testar é que ambas as funções são chamadas na sequência correta.

No entanto, isso não me impede de testar minhas funções texCompilere pdfMerger. Eles se parecem com isso:

var tex2Pdf = Promise.method(function tex2Pdf(tex_doc) {
    var latex_command = 'pdflatex';
    var pdf_output_filename = path.parse(tex_doc).name + '.pdf';
    var cmd = latex_command + ' ' + tex_doc;
    var options = {
      cwd: path.resolve(tex_doc, '..') // pdflatex will only look for custom
      // cls files in the cwd and includes relative to the cwd
    };
    child_process.spawn(cmd, options)
      .on('end', function() {
        console.log('tex2Pdf finish');
        debugger;
        return path.resolve(tex_doc, '..', pdf_output_filename);
      })
      .on('error', function(e) {
        throw e;
      });
});


var mergeTwoPdf = Promise.method(function mergeTwoPdf(pdf_files) {
  var output_file = randomId() + '.pdf';
  var cmd = 'gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=' + output_file + ' ' + pdf_files[0] + ' ' + pdf_file[1];
  child_process.spawn(cmd)
    .on('finish', function() {
      return output_file;
    })
  .on('error', function(e) {
    throw (e);
  });
});

Essa é a coisa real e é mais difícil testar. Preciso zombar de child_process.spawnum espião para garantir que ele seja chamado com os argumentos corretos, mas na verdade não faz nada, porque não quero mesclar nenhum arquivo PDF ao executar os testes e minhas zombarias precisam emitir o eventos corretos para que a função não fique presa.

Esses são os problemas que eu teria se não injetasse a dependência no meu primeiro trecho e usasse essas funções. E realmente parece que estou empurrando o problema mais para baixo sem resolvê-lo.

Estou entendendo mal a injeção de dependência? Estou fazendo errado?

carregado com mola
fonte

Respostas:

8

Você está perdendo o ponto de testar, parece.

Isso é trivial para testar: injetar zombarias ou espiões como o texCompiler e o pdfMerger é um pedaço de bolo, porque a função realmente não faz muito. Tudo o que posso testar é que ambas as funções são chamadas na sequência correta.

Impressionante! Se a função estiver executando um trabalho trivial, o teste deverá ser trivial. Da mesma forma, se toda essa função realmente estiver chamando A e B, você não estará validando grande parte do seu código.

O ponto principal dos bons testes é isolar o que você está testando; portanto, quando o teste falhar, você poderá saber melhor o que deu errado. A injeção de dependência ajuda nisso.

E realmente parece que estou empurrando o problema mais para baixo sem resolvê-lo.

Claro, nesse caso, você não está "resolvendo" porque seu código tem dependências desagradáveis ​​e difíceis de isolar. Você precisa trabalhar com o sistema de arquivos. Você precisa chamar esses processos externos. Às vezes, essa é a natureza da besta.

A injeção de dependência não remove dependências, apenas as inverte para que você possa escolher o que são. Isso ajuda a isolar o código para teste. Ajuda o código a ser mais flexível quando essas dependências inevitavelmente mudam. Mas isso não os mata - seu código ainda precisa ser capaz de fazer as mesmas coisas em algum lugar .

Telastyn
fonte
Isso faz sentido. Há algo mais que eu possa fazer para facilitar o teste dessas dependências desagradáveis?
springloaded
2
@springloaded - apenas pragmatismo. Isolar dependências é bom, mas em um aplicativo como esse, não faz muito sentido. Os testes de integração são piores que os testes de unidade (demoram mais, são mais frágeis, são mais difíceis de vincular a uma causa), mas se os testes de unidade não lhe derem muito, talvez não valham o tempo.
Telastyn
2

Eu acho que é o teste que você está entendendo mal, se é que alguma coisa.

Eu testaria a criação do seu arquivo PDF criando um arquivo PDF e comparando-o com um conhecido.

Eu testaria a mesclagem de arquivos PDF mesclando dois arquivos PDF e comparando a saída com, novamente, um arquivo válido.

Ewan
fonte
2
Então você está dizendo que eu deveria fazer testes de integração nesse código, mas nenhum teste de unidade? Nesse contexto, a injeção de dependência faz sentido?
springloaded
3
@springloaded: você faz um ponto válido. Eu diria apenas que, apenas porque o DI não faz sentido em seu cenário específico, não significa necessariamente que ele falha em todos os cenários. Lidar com bibliotecas externas é basicamente uma história de integração; o objetivo do DI é tornar essa integração cabível em vez de inseri-la diretamente no seu código.
Robert Harvey
@RobertHarvey Claro, eu ver como DI pode ser útil fora do meu caso de uso
springloaded
hmm, eu diria que não é um teste de integração nesse nível. Se desejar, você pode enviar os dados brutos e injetar a gravação no disco, se desejar mais 'unidade'.
Ewan