Como devo unidade testar um código-gerador?

votos
23

Esta é uma pergunta difícil e open-ended Eu sei, mas eu pensei que eu ia jogá-lo no chão e ver se alguém tinha alguma sugestões interessantes.

Eu desenvolvi um gerador de código que tem interface pitão para o nosso código C ++ (gerada através SWIG) e gera o código necessário para expor esta como WebServices. Quando eu desenvolvi este código eu fiz isso usando TDD, mas eu encontrei meus testes a ser frágil como o inferno. Porque cada teste essencialmente queria verificar que para um determinado trecho de código de entrada (que passa a ser um cabeçalho C ++) eu ia ficar um determinado trecho de código emitido eu escrevi um pequeno motor que lê definições de teste a partir de arquivos de entrada XML e gera teste casos de essas expectativas.

O problema é que eu temo ir para modificar o código de todo. Isso eo fato de que a unidade testa em si são um: complexo, e b: quebradiços.

Então, eu estou tentando pensar em abordagens alternativas para este problema, e parece-me que talvez estou enfrentá-lo da maneira errada. Talvez eu precise de se concentrar mais no resultado, ou seja: se o código que geram realmente executar e fazer o que eu quero que ele, em vez de, não o código de olhar a maneira que eu quero que ele.

Alguém tem alguma experiência de algo semelhante a isto que se importaria de compartilhar?

Publicado 14/08/2008 em 14:59
fonte usuário
Em outras línguas...                            


8 respostas

votos
12

Eu comecei a escrever um resumo da minha experiência com meu próprio gerador de código, em seguida, fui para trás e re-ler a pergunta e descobriu que já tinha tocado as mesmas questões a si mesmo, concentrar-se nos resultados de execução em vez do código de layout / look.

O problema é que isso é difícil de testar, o código gerado pode não ser adequado para realmente executar no ambiente do sistema de teste de unidade, e como você codificar os resultados esperados?

Descobri que você precisa para quebrar o gerador de código em pedaços menores e teste de unidade desses. Unidade de teste de um gerador de código completo é mais como testes de integração de teste de unidade, se você me perguntar.

Respondeu 14/08/2008 em 15:04
fonte usuário

votos
5

Lembre-se que "o teste de unidade" é apenas um tipo de teste. Você deve ser capaz de teste de unidade os internos peças do seu gerador de código. O que você está realmente vendo aqui é o teste de nível de sistema (também conhecido como teste de regressão). Não é apenas semântica ... há mentalidades diferentes, abordagens, expectativas, etc. É certamente mais trabalho, mas você provavelmente precisará morder a bala e criar um conjunto de testes de regressão end-to-end: arquivos fixos C ++ -> SWIG Interfaces -> módulos python -> saída conhecido. Você realmente quer verificar a entrada conhecido (código fixo C ++) contra a saída esperada (o que sai do programa Python final). Verificando os resultados do gerador código diretamente seria como comparar ficheiros de objeto ...

Respondeu 14/08/2008 em 19:15
fonte usuário

votos
0

Acho que você precisa para testar o que você está gerando mais de como você gerá-lo.

No meu caso, o programa gera muitos tipos de código (C #, HTML, SCSS, JS, etc.) que compilam em uma aplicação web. A melhor maneira que eu encontrei para reduzir erros de regressão geral é para testar a própria aplicação web, não testando o gerador.

Não me interpretem mal, ainda existem testes de unidade visitar a parte do código do gerador, mas o nosso maior para o nosso buck foi testes de interface do usuário no próprio aplicativo gerado.

Desde que nós estamos gerando-lo nós também gerar uma abstração agradável em JS que podemos usar para testar programaticamente o aplicativo. Seguimos algumas ideias delineadas aqui: http://code.tutsplus.com/articles/maintainable-automated-ui-tests--net-35089

A grande parte é que ele realmente testa a sua end-to-end do sistema, de geração de código para o que você está gerando na verdade. Uma vez que um teste falhar, é fácil segui-lo de volta para onde o gerador quebrou.

É muito doce.

Boa sorte!

Respondeu 20/07/2015 em 04:10
fonte usuário

votos
0

Minha recomendação seria a de descobrir um conjunto de resultados de entrada-saída conhecidos, como alguns casos mais simples que você já tem no lugar, e teste de unidade do código que é produzido . É inteiramente possível que, como você mudar o gerador que a seqüência exata que é produzido pode ser ligeiramente diferente ... mas o que você realmente se importa é se ele é interpretado da mesma forma. Assim, se você testar os resultados como você iria testar esse código se fosse o seu recurso, você vai descobrir se conseguir as formas desejadas.

Basicamente, o que você realmente quer saber é se seu gerador irá produzir o que você espera sem testar fisicamente todas as combinações possíveis (também: impossível). Ao garantir que o seu gerador é consistente nos caminhos que você espera, você pode sentir-se melhor que o gerador vai ter sucesso em situações cada vez mais complexas.

Desta forma, você também pode construir-se um conjunto de testes de regressão (testes de unidade que precisa para se manter funcionando corretamente). Isso ajudará você a se certificar de que as alterações ao seu gerador não está quebrando outras formas de código. Quando você encontrar um bug que os testes de unidade não pegar, você pode querer incluí-lo para impedir a ruptura similar.

Respondeu 26/07/2010 em 00:56
fonte usuário

votos
0

O teste de unidade é apenas isso testando uma unidade específica. Então, se você está escrevendo uma especificação para a classe A, é ideal se a classe A não tem as versões reais concretas de classe B e C.

Ok eu notei depois a tag para esta questão inclui C ++ / Python, mas os princípios são os mesmos:

    public class A : InterfaceA 
    {   
      InterfaceB b;

      InterfaceC c;

      public A(InterfaceB b, InterfaceC c)   {
          this._b = b;
          this._c = c;   }

      public string SomeOperation(string input)   
      {
          return this._b.SomeOtherOperation(input) 
               + this._c.EvenAnotherOperation(input); 
      } 
    }

Porque o sistema acima A injeta interfaces para sistemas B e C, você pode unidade de teste apenas sistema A, sem ter a funcionalidade real que está sendo executado por qualquer outro sistema. Este é o teste de unidade.

Aqui está uma maneira inteligente para se aproximar de um sistema desde a criação até a conclusão, com um Quando especificação diferente para cada peça de comportamento:

public class When_system_A_has_some_operation_called_with_valid_input : SystemASpecification
{
    private string _actualString;

    private string _expectedString;

    private string _input;

    private string _returnB;

    private string _returnC;

    [It]
    public void Should_return_the_expected_string()
    {
        _actualString.Should().Be.EqualTo(this._expectedString);
    }

    public override void GivenThat()
    {
        var randomGenerator = new RandomGenerator();
        this._input = randomGenerator.Generate<string>();
        this._returnB = randomGenerator.Generate<string>();
        this._returnC = randomGenerator.Generate<string>();

        Dep<InterfaceB>().Stub(b => b.SomeOtherOperation(_input))
                         .Return(this._returnB);
        Dep<InterfaceC>().Stub(c => c.EvenAnotherOperation(_input))
                         .Return(this._returnC);

        this._expectedString = this._returnB + this._returnC;
    }

    public override void WhenIRun()
    {
        this._actualString = Sut.SomeOperation(this._input);
    }
}

Portanto, em conclusão, uma única unidade / especificação pode ter vários comportamentos, ea especificação cresce à medida que desenvolve a unidade / sistema; e se o seu sistema em teste depende de outros sistemas concretos dentro dela, cuidado.

Respondeu 20/05/2010 em 00:17
fonte usuário

votos
0

Só queria salientar que você ainda pode alcançar o teste de granulação fina enquanto verifica os resultados: você pode testar pedaços individuais de código aninhando-los dentro de algum código de configuração e verificação:

int x = 0;
GENERATED_CODE
assert(x == 100);

Desde que você tenha o seu código gerado montado a partir de pequenos pedaços, e os pedaços não mudam com freqüência, você pode exercer mais condições e testar um pouco melhor, e espero evitar ter todos os seus testes quebrar quando você alterar detalhes de um pedaço.

Respondeu 16/09/2008 em 10:41
fonte usuário

votos
0

Se você estiver executando em * nux você pode considerar despejar o framework unittest em favor de um script ou makefile. em janelas que você pode considerar a construção de uma função / shell aplicativo que executa o gerador e, em seguida, usa o código (como outro processo) e unittest isso.

Uma terceira opção seria a de gerar o código e, em seguida, criar um aplicativo a partir dele que inclui nada além de um unittest. Mais uma vez você precisaria de um shell script ou outros enfeites para executar este para cada entrada. Quanto à forma de codificar o comportamento esperado, ocorre-me que isso poderia ser feito em muito da mesma forma como faria para o código C ++ apenas usando a interface gerada ao invés do C ++.

Respondeu 14/08/2008 em 16:46
fonte usuário

votos
0

Sim, os resultados são a única coisa que importa. A tarefa real está escrevendo um quadro que permite que o código gerado para executar de forma independente ... gastar o seu tempo lá.

Respondeu 14/08/2008 em 15:38
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more