IEA - Uma estratégia de teste baseada em arquivos para transformações de documentos
por Douglas Campbell, Engenheiro Principal
I/E/A - Entrada/Esperado/Actual
Se você puder fornecer implementações para o conjunto de funções abaixo,
// defines how to go from file to an instance of IN
abstract IN loadInput(File input);
// defines how to go from file to OUT instances
// throw FNF whenever there’s no expected file.
abstract OUT loadOutput(File expected) throws FileNotFoundException;
// invoked upon missing expected file or if actual != expected
abstract void storeResult(OUT result, File resultFile);
muito pode ser feito para tirar a drogaria das transformações de documentos de teste e manter um conjunto robusto de testes.
Em resumo, torna-se trivial escrever casos de teste como arquivos em vez de funções.
Eu prefiro muito mais esta abordagem do que testar a transformação de documentos do que usar o código de teste. Primeiro, um pouco de fundo.
Antecedentes
Tanto do código que escrevi ao longo dos anos tem no seu núcleo a transformação de documentos de um formato em outro. Por documentos, estou falando de qualquer variedade de json, xml, avro, tab delimitado, yaml, qualquer coisa - qualquer coisa e tudo para qualquer coisa e tudo e de volta novamente.
Quando se pede para codificar coisas como esta, muitos olhos do dev simplesmente rolam para o horror de escrever algo tão trivial como cartografar um campo para outro, dar-lhe um casquilho superior, fundi-lo com outro valor lido a partir de alguma fonte de dados externa, e depois sair para outra representação.
Essa nova representação pode ter pouca semelhança com o documento inicial. Essa é a natureza da besta.
Em suma, os requisitos que impulsionam as transformações de dados são completamente arbitrários!
Acho que esta é a razão pela qual alguns devs detestam estas coisas. Eles tomam atalhos e atiram coisas para o código de teste que se parecem com isto...
//CHECKSTYLE:OFF
private static final String CLICK = "a horrendously ugly line of input from an access log";
//CHECKSTYLE:ON
E quando chegar a hora de testar uma "impressão" adivinhe o que acontece. Ugh - testar a entrada em linha como variáveis de string. Sentido de aranha imediatamente activado após a supressão do checkstyle!
Uma coisa que aprendi é que onde quer que haja medo e evasão, é exactamente onde é necessária alguma preguiça excessivamente enérgica. Afaste-se. Simplifique tudo e livre-se de resmas e resmas de lógica e ou código duplicados. Escreva uma função para correr sobre uma pasta inteira cheia de testes
// test folder of tests and fail entire run if any fail.
public void testFolder(File testfolder, Function<IN, OUT> converter) {
for (File test: testfolder.listFiles(testFileFilter)) {
if (!testSingle(test, converter)) {
fail("expected != actual - actual " +
"results saved in .actual file");
};
}
}
Então e este testeFunção única? Muito simples com as três funções definidas no início do blog.
// test a single test
private boolean testSingle(File test, Function converter) {
IN input = loadInput(test);
OUT actual = converter.apply(input);
OUT expected = null;
// only create if no expected file or result is different.
File actualFile = new File(test.getParent(),
test.getName() + ".actual");
try {
expected = loadOutput(new File(test.getParent(),
test.getName()
+ ".expected"));
} catch (FileNotFoundException ex) {
// we haven't got expected file - no biggie
// we can turn this into an expected file once
// satisfied with it.
storeResult(actual, actualFile);
}
return false;
// we've got something to compare
if (!actual.equals(expected)) {
// fail and save the actual file for command
// line diffing
storeResult(actual, actualFile);
return false;
}
return true;
}
Diversos benefícios desta abordagem
Para documentos baseados em texto e ou ascii, a utilidade dif está imediatamente disponível para comparar falhas nos testes
Você não precisa escrever código de teste para gerar qual é o resultado esperado. Basta adicionar o seu registro de amostra à pasta de testes, executar os testes e a saída real é gerada para um novo arquivo com extensão .real
Você tem uma maneira imediata de executar o seu código sobre os registros que lhe causaram problemas na produção. Mais uma vez, coloque o registro problemático em sua pasta de teste e execute o teste.
Se a sua equipa decidir construir um novo conversor ou usar uma biblioteca json diferente, todos os seus testes são expressos como ficheiros independentes do idioma. Você pode levá-los adiante.
Coisas para ir logo à frente
Primeiro, tenha cuidado para tornar a sua lógica de transformação sem estados. Essencialmente, as transformações de documentos que envolvem chamadas fora de processo precisam ser retrabalhadas a fim de separar a recuperação de dados brutos do que é feito a ela.
Em segundo lugar, comece a fazer isso logo no início do ciclo de vida do módulo de código ou aplicativo. Os apetites são normalmente muito escassos para dar voltas e voltar a trabalhar nos velhos e nojentos testes.
Próximo passo
Um próximo passo para nós é empacotar isto como um pacote de teste de código aberto. Quem sabe, isso pode acontecer ainda mais cedo se tivermos um pouco de interesse