Por que não está testando um idioma um recurso suportado no nível da sintaxe?

36

Você pode encontrar uma lista interminável de blogs, artigos e websites que promovem os benefícios do teste unitário do seu código-fonte. É quase garantido que os desenvolvedores que programaram os compiladores para Java, C ++, C # e outras linguagens tipificadas usaram o teste de unidade para verificar seu trabalho.

Então por que, apesar de sua popularidade, o teste está ausente da sintaxe dessas linguagens?

A Microsoft introduziu LINQ para C # , então por que eles não puderam adicionar testes?

Não estou pensando em prever quais seriam essas mudanças de linguagem, mas para esclarecer por que elas estão ausentes no início.

Como exemplo: Sabemos que você pode escrever um loop for sem a sintaxe da instrução for . Você pode usar as declarações while ou if / goto . Alguém decidiu que uma declaração for era mais eficiente e a introduziu em um idioma.

Por que o teste não seguiu a mesma evolução das linguagens de programação?

    
por cgTag 03.07.2014 / 18:55
fonte

6 respostas

36

Como acontece com muitas coisas, o teste de unidade é melhor suportado no nível de biblioteca, não no nível de idioma. Em particular, o C # possui inúmeras bibliotecas de Testes Unitários disponíveis, bem como coisas que são nativas do .NET Framework como Microsoft.VisualStudio.TestTools.UnitTesting .

Cada biblioteca de Testes Unitários tem uma filosofia e sintaxe de testes um pouco diferentes. Todas as coisas sendo iguais, mais escolhas são melhores que menos. Se os testes de unidade fossem introduzidos no idioma, você estaria trancado nas opções do designer de idiomas ou estaria usando ... uma biblioteca e evitando os recursos de teste de idiomas.

Exemplos

  • Nunit - Estrutura de teste de unidade de propósitos gerais e idiomática que aproveita ao máximo os recursos de linguagem do C #.

  • Moq - Estrutura de simulação que aproveita ao máximo as expressões lambda e as árvores de expressão, sem uma metáfora de registro / reprodução.

Existem muitas outras opções. Bibliotecas como o Microsoft Fakes podem criar "shims ..." que não exigem que você escreva suas classes usando interfaces ou métodos virtuais.

Linq não é um recurso de linguagem (apesar do nome)

Linq é um recurso de biblioteca. Temos muitos recursos novos na linguagem C # em si, gratuitamente, como expressões lambda e métodos de extensão, mas a implementação real do Linq está no .NET Framework.

Existe algum açúcar sintático que foi adicionado ao C # para tornar as instruções do linq mais limpas, mas que o açúcar não é necessário para usar o linq.

    
por 03.07.2014 / 19:03
fonte
21

Existem muitas razões. Eric Lippert afirmou várias vezes que a razão feature X não está em C # é porque ela não está em seu orçamento. Os designers de linguagem não têm uma quantidade infinita de tempo nem dinheiro para implementar as coisas, e cada novo recurso tem custos de manutenção associados a ele. Manter a linguagem tão pequena quanto possível não é apenas mais fácil para os projetistas de linguagem - também é mais fácil para qualquer um escrever implementações e ferramentas alternativas (por exemplo, IDEs) Além disso, quando algo é implementado em termos de linguagem e não parte dela, portabilidade de graça. Se o teste de unidade estiver sendo implementado como uma biblioteca, você só precisará escrevê-lo uma vez e ele funcionará em qualquer implementação em conformidade do idioma.

É importante notar que D tem suporte em nível de sintaxe para testes unitários . Eu não sei por que eles decidiram jogar isso, mas vale a pena notar que o D deve ser uma "linguagem de programação de sistemas de alto nível". Os projetistas queriam que fosse viável para o tipo de código inseguro e de baixo nível em que o C ++ era tradicionalmente usado, e um erro no código inseguro é um comportamento incrivelmente caro e indefinido. Então, suponho que faça sentido para eles gastar um esforço extra em qualquer coisa que ajude a verificar se algum código inseguro funciona. Por exemplo, você pode impor que apenas determinados módulos confiáveis executem operações inseguras, como acessos de matriz não verificados ou aritmética de ponteiros.

O desenvolvimento rápido também era uma prioridade para eles, tanto que eles criaram uma meta de design que o código D compila rápido o suficiente para torná-lo utilizável como uma linguagem de script. Testes de unidade de cozimento diretamente na linguagem, para que você possa executar seus testes apenas passando uma sinalização extra para o compilador, ajuda com isso.

No entanto, acho que uma biblioteca de testes unitários excelente faz muito mais do que apenas encontrar alguns métodos e executá-los. Pegue o QuickCheck do Haskell, por exemplo, que permite testar coisas como "para todos os xey, f (x, y) == f (y, x) ". O QuickCheck é melhor descrito como um teste de unidade generator e permite que você teste as coisas em um nível mais alto do que "para esta entrada, estou esperando esta saída". O QuickCheck e o Linq não são tão diferentes - são ambos idiomas específicos do domínio. Então, em vez de apostar no suporte a testes de unidade em um idioma, por que não adicionar os recursos necessários para tornar as DSLs práticas? Você acabará não apenas com o teste de unidade, mas com uma linguagem melhor como resultado.

    
por 03.07.2014 / 20:35
fonte
13

Porque o teste, e particularmente o desenvolvimento orientado a testes, é um fenômeno profundamente contra-intuitivo.

Quase todos os programadores começam suas carreiras acreditando que são muito melhores em gerenciar a complexidade do que realmente são. O fato de que mesmo o maior programador não pode escrever programas grandes e complexos sem erros graves, a menos que eles usem muitos testes de regressão é seriamente decepcionante e até mesmo vergonhoso para muitos praticantes. Daí a desinclinação prevalente contra os testes regulares, mesmo entre profissionais que já deveriam saber melhor.

Acredito que o fato de os testes religiosos estarem lentamente se tornando mais comuns e esperados se deve em grande parte ao fato de que, com a explosão na capacidade de armazenamento e na capacidade de computação, sistemas maiores estão sendo construídos - e sistemas extremamente grandes são particularmente propensos a colapso da complexidade que não pode ser gerenciado sem a rede de segurança dos testes de regressão. Como resultado, até mesmo desenvolvedores particularmente obstinados e iludidos admitem a contragosto que precisam de testes e sempre precisarão de testes (se isso soa como a confissão em uma reunião de AA, isso é bastante intencional - precisar de uma rede de segurança é psicologicamente difícil de admitir para muitos indivíduos).

A maioria dos idiomas populares hoje em dia é anterior a essa mudança de atitude, então eles têm pouco suporte interno para testes: eles têm assert , mas não há contratos. Tenho quase certeza de que, se a tendência persistir, os futuros idiomas terão mais suporte no idioma do que no nível da biblioteca.

    
por 03.07.2014 / 21:25
fonte
6

Muitas linguagens têm suporte para testes. C afirma que são testes que o programa pode falhar. É aí que a maioria das linguagens param, mas Eiffel e mais recentemente Ada 2012 têm pré-variantes (coisas que os argumentos para uma função devem passar) e pós-variantes (coisas que a saída de uma função deve passar), com Ada oferecendo a capacidade de referenciar os argumentos iniciais. pós-variante. A Ada 2012 também oferece invariantes de tipos, portanto, sempre que um método é chamado em uma classe Ada, a invariante de tipo é verificada antes do retorno.

Esse não é o tipo de teste completo que uma boa estrutura de teste pode oferecer, mas é um tipo importante de teste que as linguagens podem oferecer melhor suporte.

    
por 04.07.2014 / 01:32
fonte
2

Alguns proponentes de linguagens funcionais strongmente tipificadas argumentariam que esses recursos de linguagem reduzem ou eliminam a necessidade de testes unitários.

Dois, imho, bom exemplo disso para isso é de F # para diversão e lucro aqui e aqui

Pessoalmente, ainda acredito no valor dos testes unitários, mas existem alguns pontos válidos. Por exemplo. Se um estado ilegal não é representável no código, então não é apenas desnecessário escrever um teste de unidade para este caso, é impossível.

    
por 04.07.2014 / 01:13
fonte
2

Eu diria que você perdeu a adição de alguns dos recursos necessários porque eles não estavam sendo destacados como para testes unitários .

Por exemplo, o teste de unidade em C # é principalmente orientado pelo uso de atributos. O recurso Atributos personalizados fornece um mecanismo de extensão avançado que permite estruturas como NUnit para iterar e competir, com coisas como Baseada na teoria e Testes parametrizados.

Isso me leva ao segundo ponto principal - não sabemos o suficiente sobre o que faz uma boa testabilidade para assá-lo em um idioma. O ritmo da inovação nos testes é muito mais rápido do que outros construtos da linguagem, por isso precisamos ter mecanismos flexíveis em nossas linguagens para deixar a liberdade de inovar.

Eu sou um usuário, mas não sou fanático por TDD - é muito útil em alguns casos, especialmente para melhorar seu design thinking. Não é necessariamente útil para sistemas legados maiores - testes de nível superior com dados e automação bons provavelmente renderão mais benefícios junto com uma cultura de inspeção de código strong .

Outra leitura relevante:

por 04.07.2014 / 03:45
fonte