Qual è l'approccio migliore per il test dell'unità di un'unità complessa come un compilatore?Unit test di un compilatore
Ho scritto un paio di compilatori e interpreti nel corso degli anni, e lo faccio trovare questo tipo di codice molto difficile da testare in un buon modo.
Se prendiamo qualcosa come la generazione di Abstract Syntax Tree. come metteresti alla prova questo usando TDD?
I piccoli costrutti possono essere facili da testare. ad es. qualcosa del tipo:
string code = @"public class Foo {}";
AST ast = compiler.Parse(code);
Dal momento che non genererà un sacco di nodi.
Ma se in realtà voglio provare che il compilatore in grado di generare un AST per qualcosa di simile a un metodo:
[TestMethod]
public void Can_parse_integer_instance_method_in_class()
{
string code = @"public class Foo { public int method(){ return 0;}}";
AST ast = compiler.Parse(code);
Cosa vorresti valere su? Definire manualmente un AST che rappresenta il codice dato e fare un'affermazione secondo cui l'AST generato è conforme all'AST definito manualmente sembra orribilmente combiante e potrebbe persino essere soggetto a errori.
Quindi quali sono le migliori tattiche per TDD'ing scenari complessi come questo?
È solo uno dei numerosi esempi del perché i test di unità sono inutili e inferiori, e l'attenzione dovrebbe essere concentrata su un test di integrazione. TDD è per CRUD, non per le cose serie. Per i compilatori, il test del codice generato casualmente è di gran lunga l'approccio migliore possibile. Ad esempio: http://www.cs.utah.edu/~regehr/papers/pldi11-preprint.pdf –
Potresti anche essere interessato ad un approccio superiore alla costruzione sicura del compilatore: http://compcert.inria.fr/doc /index.html - le specifiche formali sono sicuramente una migliore garanzia di qualità rispetto a qualsiasi test possibile. –
@peer, di quali "metodi" stai parlando? Se viene generato un parser (si pensi a 'bison' e allo stesso modo), si avrà una grammatica monolitica e una pila illeggibile di un codice generato. Niente da testare oltre alla grammatica nel suo complesso. Se si tratta di un parser ricorsivo di discendenza scritto a mano, è ancora più difficile eseguire il test unitario (si veda, ad esempio, il codice sorgente Clang e provare a pensare a come simulare ASTContext e un flusso di input per ogni piccola voce del parser). Il test delle unità è davvero inutile per qualsiasi codice ragionevolmente complicato. –