Pular para o conteúdo principal

Test Driven em C++ cross-compiled é possível

afe!

Enquanto não rola um estudo retrospectivo maneiro, aqui vão minhas primeiras impressões.

  1. por mais que pareça andar de ré, tenha um porting para o sistema do desenvolvedor, onde a compilação não é cruzada;
  2. não exija frameworks xUnit e faça bilhões de programecos se necessário;
  3. guarde no coração que não é preciso reflection nem mesmo shared objects para aplicar dependency injection;
  4. não despreze a tediosa tarefa de testar parte a parte, pequena parte a pequena parte;
  5. não tenha escrúpulos para satisfazer dependências aleatórias com stubs.
A maior revelação para mim nesse processo foram os pontos 2, 3 e 5 acima.

Após um longo tempo digerindo o problema, e após diversas discussões sobre que framework de testes usar, por fim completei a tarefa da forma mais tosca possível: cada teste é um programa executável individual.  O projeto usa cmake para configurar a construção, e o ctest funciona legal com essa lista de programas de teste.

O maior esforço nesse processo foi refatorar o código o suficiente para conseguir testar uma unidade isolada realmente. A dificuldade percebida pela equipe desde o início era devido ao acoplamento entre as diversas unidades teóricas do sistema, e a quantidade de código de inicialização de coisas irrelevantes ao teste. Eventualmente tropecei na idéia de dependency injection. Apesar de ser algo que ocorre em super frameworks de componentes empresariais, o conceito é simples e se mostrou aplicável mesmo em um sistema ligado estaticamente, desde que as unidades fossem desacopladas o suficiente uma da outra. No nosso caso, isso se dá na forma de um Factory central para todo o sistema, pré-existente ao meu esforço.

Claro que esse desacoplamento "suficiente" é um graal e nunca ocorre, especialmente em um ambiente tão confortável quanto o da ligação estática -- onde tudo se junta no final numa grande sopa de elementos. Ocorreu que algumas funções que eram parte da unidade mas não eram exercitadas faziam referência a algum nome que acabava indefinido no programa final -- função ou variável extern. Eventualmente realizei que isso simplesmente não tem solução prática e aceitei a seguinte possibilidade: se uma unidade não compila sem uma função de outra unidade, e a função é irrelevante para o teste, vamos enfiar um stub na jogada e ponto final. De certa forma, isso pode ser considerado um Mock do mundo pré-componentização.

Acaba que, por fim, se não lindamente xUnit, meu objeto de manutenção agora possui uma suite de testes de unidade. Entre os vinte e trinta testes bem básicos que eu bolei pra começar, já descobri umas cinco funções implementadas diferente do que o meu projeto previa. O cmake suporta bem a lista de testes e gera um relatoriozinho bem legal.

Comentários

Postagens mais visitadas deste blog

Análise vs. Projeto

Eu diria que a ruptura entre Análise e o Projeto acontece quando acabam as considerações sobre o problema e começam as considerações sobre a solução. Em outras palavras, o papel do Analista é considerar o problema, enquanto o papel do Projetista é o de considerar a solução. Assim, o Analista aparece quando um problema aparece, e dá lugar ao Projetista quando o problema foi devidamente analisado, restando produzir para ele uma solução. E diria que a ruptura entre a Especificação e a Implementação acontece quando acabam os artefatos para consumo por humanos e começam os artefatos para consumo por máquinas. Em outras palavras, o papel do Especificador é produzir artefatos para consumo por humanos, enquanto o papel do Implementador é produzir artefatos para consumo por máquinas. Assim, o Especificador aparece quando a atividade é produzir efeitos sobre humanos, e dá lugar ao Implementador quando os humanos estão devidamente satisfeitos e a atividade prossegue para produzir efeito...

Por que goto é considerado prejudicial?

Recentemente, o Fabiano Vasconcelos abriu uma discussão no Grupo de Usuários de C e C++: De cara eu vi algo aqui um pouco estranho, se que o amigo Márcio me permite comentar: que muitos programadores, inclusive eu (se que posso ser rotulado como programador) foram instruídos com o princípio de NUNCA usar o goto, por ser considerado um mau estilo de programação. Durante a discussão, o Eduardo Vieira puxou um artigo da KernelTrap sobre uma discussão similar ocorrida no grupo de desenvolvimento do Linux, onde Robert Wilken disse o seguinte: In general, if you can structure your code properly, you should never need a goto, and if you don't need a goto you shouldn't use it. It's just "common sense" as I've always been taught. Unless you're intentionally trying to write code that's harder for others to read. É preciso colocar a máxima "goto considered harmful" na perspectiva histórica adequada. Acredito que praticamente todo programador trein...

Servidor e usuário COM na mesma "solution"

 Um dos objetivos da próxima iteração no meu projeto atual é quebrar o sistema em mais pedaços, e levantar os pedaços um nível de abstração até componentes COM. Uma das consequências é substituir diversas ligações mais estáticas e torná-las ligações mais dinâmicas através do COM. Tipicamente, um programa usuário de um componente COM, escrito para o Visual Studio, usará a diretiva #import para incluir na unidade de tradução C++ as declarações necessárias. Isso implica que o componente deve estar instalado na máquina que constrói o programa. O que fazer quando o componente em questão está no mesmo lote de construção que o programa usuário? A solução inicial é criar uma dependência de construção tal que o componente seja sempre construído primeiro, e usar um post-build event ao projeto do componente para registrá-lo no sistema. Desse modo, na construção do programa, o ambiente estará correto. Eu considero este arranjo prejudicial. Minha melhor razão é esta: o servidor de const...