Teste de unidade em estruturas de visualização (gráficos 3D)

8

Este é um acompanhamento para esta pergunta. Eu estava perguntando como fazer testes de unidade quando você tem uma biblioteca de algoritmos científicos. Eu tenho um problema semelhante agora, mas com um projeto diferente.

Estou trabalhando em uma abstração de estrutura de mecanismo de gráficos 3D para DirectX, OpenGl, WebGl, Silverlight, WPF e basicamente qualquer API 3D existente, em C #, chamada Rendering .NET . O cenário é o seguinte, o projeto está sendo desenvolvido por uma pequena equipe de colegas meus, todos trabalhando no mesmo escritório. Somos todos cientistas da computação, pouco interessados ​​na comunidade e nas práticas de engenharia de software. Eu até tive dificuldade em convencer a mudar do Subversion para o Git e publicar o projeto no Codeplex. O projeto está ficando grande (cerca de 80 mil linhas de C #) e agora abrange a maior parte do DirectX e do OpenGl até o Shader Model 5.0, portanto, não é um projeto único como o descrito na pergunta vinculada. Também queremos incentivar a comunidade de código aberto a colaborar.

Até agora, testamos escrevendo pequenos aplicativos que envolvem a inicialização de todos os dispositivos e recursos, a criação de uma cena e o desenho. O problema é que não sei como diferenciá-lo, quero dizer, como projetar pequenos casos de teste que testam recursos específicos da estrutura, como alocação de recursos, mosaico primitivo, compilação de sombreamento, etc., sem ter que recriar o inicialização do mecanismo inteiro. Para alguns desses casos, posso pensar em zombarias úteis, mas, em geral, como posso testar a correção de um algoritmo de renderização quando a saída é visual (uma imagem ou uma cena animada)? No momento, a coisa mais inteligente que encontramos é renderizar a mesma cena para um renderizador de software, DirectX e OpenGl, e compará-los pixel por pixel,

Então, qual é a abordagem de teste "correta" aqui?

Alejandro Piad
fonte
Você pode implementar níveis de tolerância em sua comparação pixel por pixel, mas isso seria um jogo de falsos negativos versus falsos positivos e ainda estaria testando parcialmente a implementação do DirectX / OpenGL, não apenas o seu código (portanto, não é realmente um teste de unidade , por si só). Na maioria dos cenários "normais", você deseja zombar da API de destino e garantir que ela seja chamada de acordo com suas suposições; se seu código de renderização estiver chamando as várias APIs diretamente, talvez você possa introduzir sua própria camada intermediária (ridicularizável), embora com uma penalidade de desempenho.
Daniel B
Devo acrescentar também que acredito que a resposta do Doc Brown é essencialmente correta; você deve (idealmente) ter partes realmente independentes que possam ser testadas isoladamente. Na realidade, porém, se você não arquitetou sua solução desde o início para teste de unidade, isso pode ser difícil de ser adaptado.
Daniel B
@ DanielB: exatamente o que eu pensava ao ler que 80K de código já existe. Este é um código legado - como Michael Feathers define - código sem testes. E o livro canônico para esse tipo de situação é sempre amazon.de/Working-Effectively-Legacy-Robert-Martin/dp/…
Doc Brown

Respostas:

8

A abordagem de teste "correta" seria dissociar sua lógica de desenho das chamadas do DirectX ou do OpenGL, para que você possa zombar da última (e, como outro caso de uso, use a mesma lógica de negócios do DirectX ou do OpenGL).

Você não deseja testar se o DirectX ou OpenGL está funcionando corretamente - esse é o trabalho da Microsoft. Você também deseja que o código de inicialização do dispositivo seja gravado e testado apenas uma vez (e depois reutilizado), para que um teste manual dessa parte seja acessível. Se você quiser testar essa peça automaticamente também, use sua abordagem pixel por pixel, mas com um desenho de teste muito simples. Portanto, concentre-se em escrever testes de regressão automatizados para sua lógica de desenho desacoplada, que trará mais benefícios.

Portanto, a idéia é dividir seu código em componentes realmente independentes - sua inicialização do DirectX não deve ser acoplada à lógica de desenho e a lógica de desenho não deve depender do DirectX - isso torna seu programa testável.

EDIT: encontrei este post anterior em testes de unidade para código OpenGL, havia apenas algumas respostas, mas talvez traga algumas informações adicionais.

Doc Brown
fonte
Parece que essa abordagem não abrangeria grande parte do conteúdo desta biblioteca.
Kris Van Bael
@KrisVanBael: talvez, talvez não, você esteja apenas adivinhando. Mas fique à vontade para sugerir melhor uma abordagem de teste, caso você esteja certo.
Doc Brown
Obrigado @DocBrown. A estrutura é dissociada das implementações específicas da API, por meio de muitas interfaces, e estamos usando injeção de dependência em todos os lugares para resolver gerenciadores de recursos, renderizadores, compiladores e outros, para que você possa, por exemplo, criar uma malha com o DirectX e visualizá-lo em OpenGL. Então você diz que eu deveria escrever um tipo de "implementação de software" que zomba de toda a estrutura e teste de unidade lá? Parece legal. Concordo que não devo testar o DirectX nem o OpenGL, apenas minha camada intermediária.
Alejandro Piad 28/02
@AlejandroPiad: como comer um elefante? Uma mordida de cada vez. Adicione testes ao longo do seu roteiro de desenvolvimento, testando as partes do seu programa que você irá alterar em seguida. Seu programa não foi criado em uma semana e, portanto, sua estrutura de teste não será criada nesse período.
Doc Brown
1
@DocBrown muito obrigado. Eu acho que entendi. Começarei a zombar das partes vitais primeiro e tentarei que tudo o que adicionamos seja projetado com testes em mente.
Alejandro piad