vale - Código C do Teste Unitário




teste unitário vale a pena (20)

Eu trabalhei em um sistema embarcado neste verão escrito em linha reta C. Foi um projeto existente que a empresa para a qual trabalhei assumiu. Tornei-me bastante acostumado a escrever testes unitários em Java usando o JUnit, mas fiquei perdendo a melhor maneira de escrever testes de unidade para código existente (que precisava de refatoração), bem como um novo código adicionado ao sistema.

Existe alguma maneira de tornar o código C simples de teste de unidade tão fácil quanto o código Java de teste de unidade com, por exemplo, JUnit ? Qualquer insight que se aplicasse especificamente ao desenvolvimento embarcado (cross-compiling para arm-linux platform) seria muito apreciado.


Atualmente estou usando o framework de teste unitário do CuTest:

http://cutest.sourceforge.net/

É ideal para sistemas embarcados, pois é muito leve e simples. Não tive problemas em fazer com que funcionasse na plataforma de destino, assim como na área de trabalho. Além de escrever os testes de unidade, tudo o que é necessário é:

  • um arquivo de cabeçalho incluído onde quer que você esteja chamando as rotinas do CuTest
  • um único arquivo 'C' adicional a ser compilado / vinculado à imagem
  • algum código simples adicionado ao principal para configurar e chamar os testes de unidade - eu só tenho isso em uma função especial main () que é compilada se UNITTEST é definido durante a construção.

O sistema precisa suportar um heap e alguma funcionalidade stdio (que nem todos os sistemas embarcados possuem). Mas o código é simples o suficiente para que você possa trabalhar em alternativas a esses requisitos, caso sua plataforma não os tenha.

Com algum uso criterioso de blocos externos "C" {} ele também suporta testar C ++ muito bem.


Caso você esteja almejando plataformas Win32 ou modo kernel do NT, você deve dar uma olhada no cfix .


Depois de ler o Minunit, achei que uma maneira melhor era basear o teste em afirmar macro, que uso muito como técnica de programa defensivo. Então eu usei a mesma ideia do Minunit misturado com a declaração padrão. Você pode ver meu framework (um bom nome poderia ser NoMinunit) no blog de k0ga



Eu não cheguei muito longe testando um aplicativo C legado antes de começar a procurar uma maneira de simular funções. Eu precisava muito de zombarias para isolar o arquivo C que quero testar dos outros. Dei uma chance ao cmock e acho que vou adotá-lo.

O Cmock verifica arquivos de cabeçalho e gera funções simuladas com base nos protótipos encontrados. Mocks permitirá que você teste um arquivo C em perfeito isolamento. Tudo o que você precisa fazer é vincular seu arquivo de teste a simuladores em vez de seus arquivos de objetos reais.

Outra vantagem do cmock é que ele validará os parâmetros passados ​​para as funções falsificadas e permitirá que você especifique qual valor de retorno os mocks devem fornecer. Isso é muito útil para testar diferentes fluxos de execução em suas funções.

Os testes consistem nas típicas funções testA (), testB () nas quais você cria expectativas, chama funções para testar e verificar afirmações.

O último passo é gerar um corredor para seus testes com unidade. O Cmock está vinculado à estrutura de teste de unidade. A unidade é tão fácil de aprender quanto qualquer outra estrutura de teste de unidade.

Vale a pena tentar e muito fácil de entender:

http://sourceforge.net/apps/trac/cmock/wiki

Atualização 1

Outra estrutura que estou investigando é a Cmockery.

http://code.google.com/p/cmockery/

É um framework C puro que suporta testes unitários e zombarias. Não tem dependência de rubi (ao contrário de Cmock) e tem pouca dependência de bibliotecas externas.

Isso requer um pouco mais de trabalho manual para configurar os mocks porque não gera código. Isso não representa muito trabalho para um projeto já existente, já que os protótipos não mudam muito: depois de ter seus mocks, você não precisará mudá-los por um tempo (esse é o meu caso). Tipagem extra fornece controle completo de zombarias. Se há algo que você não gosta, simplesmente mude o seu mock.

Não há necessidade de um corredor de teste especial. Você só precisa criar um array de testes e passá-lo para uma função run_tests. Um pouco mais de trabalho manual aqui também, mas eu definitivamente gosto da ideia de um framework autônomo independente.

Além disso, contém alguns truques bacanas que eu não conhecia.

No geral, a Cmockery precisa de um pouco mais de entendimento sobre os mocks para começar. Exemplos devem ajudá-lo a superar isso. Parece que pode fazer o trabalho com mecânica mais simples.


Eu não uso um framework, eu apenas uso o autotools "check". Implemente um "main" e use assert (s).

Meu dir de teste Makefile.am (s) se parece com:

check_PROGRAMS = test_oe_amqp

test_oe_amqp_SOURCES = test_oe_amqp.c
test_oe_amqp_LDADD = -L$(top_builddir)/components/common -loecommon
test_oe_amqp_CFLAGS = -I$(top_srcdir)/components/common -static

TESTS = test_oe_amqp

Eu uso CxxTest para um ambiente c / c ++ incorporado (principalmente C ++).

Eu prefiro o CxxTest porque ele tem um script perl / python para construir o executor de teste. Depois de uma pequena inclinação para configurá-lo (menor ainda, já que você não precisa escrever o executor de testes), é muito fácil de usar (inclui amostras e documentação útil). O maior trabalho foi configurar o 'hardware' que o código acessa para que eu pudesse testar a unidade / módulo de forma eficaz. Depois disso, é fácil adicionar novos casos de teste de unidade.

Como mencionado anteriormente, é uma estrutura de teste de unidade C / C ++. Então você precisará de um compilador C ++.

Guia do Usuário CxxTest CxxTest Wiki


Existe uma elegante estrutura de testes unitários para C com suporte para objetos simulados chamados cmocka . Requer apenas a biblioteca C padrão, funciona em uma variedade de plataformas de computação (incluindo incorporadas) e com diferentes compiladores.

Ele também tem suporte para diferentes formatos de saída de mensagem, como Subunit, Test Anything Protocol e jUnit XML reports.

O cmocka foi criado para funcionar também em plataformas embarcadas e também possui suporte ao Windows.

Um teste simples é assim:

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>

/* A test case that does nothing and succeeds. */
static void null_test_success(void **state) {
    (void) state; /* unused */
}

int main(void) {
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(null_test_success),
    };
    return cmocka_run_group_tests(tests, NULL, NULL);
}

A API é totalmente documentada e vários exemplos fazem parte do código-fonte.

Para começar com o cmocka você deve ler o artigo sobre o LWN.net: Teste unitário com objetos simulados em C

O cmocka 1.0 foi lançado em fevereiro de 2015.


Nós escrevemos o CHEAT (hospedado no GitHub ) para facilidade de uso e portabilidade.

Não tem dependências e não requer instalação ou configuração. Apenas um arquivo de cabeçalho e um caso de teste são necessários.

#include <cheat.h>

CHEAT_TEST(mathematics_still_work,
    cheat_assert(2 + 2 == 4);
    cheat_assert_not(2 + 2 == 5);
)

Os testes são compilados em um executável que cuida da execução dos testes e relata seus resultados.

$ gcc -I . tests.c
$ ./a.out
..
---
2 successful of 2 run
SUCCESS

Tem cores bonitas também.



O livro de Michael Feather, "Trabalhando Efetivamente com o Código Legado", apresenta muitas técnicas específicas para testes unitários durante o desenvolvimento do C.

Existem técnicas relacionadas à injeção de dependência que são específicas de C, que eu não vi em nenhum outro lugar.


Pessoalmente, gosto da estrutura do teste do Google .

A dificuldade real em testar o código C está quebrando as dependências dos módulos externos para que você possa isolar o código em unidades. Isso pode ser especialmente problemático quando você está tentando obter testes sobre o código legado. Nesse caso, frequentemente me encontro usando o vinculador para usar funções de stubs em testes.

Isto é o que as pessoas estão se referindo quando falam de " costuras ". Em C, sua única opção é realmente usar o pré-processador ou o vinculador para ridicularizar suas dependências.

Um conjunto de testes típico em um dos meus projetos em C pode ter esta aparência:

#include "myimplementationfile.c"
#include <gtest/gtest.h>

// Mock out external dependency on mylogger.o
void Logger_log(...){}

TEST(FactorialTest, Zero) {
    EXPECT_EQ(1, Factorial(0));
}

Observe que você está realmente incluindo o arquivo C e não o arquivo de cabeçalho . Isso dá a vantagem de acesso a todos os membros de dados estáticos. Aqui eu mexo meu logger (que pode estar em logger.o e dou uma implementação vazia. Isso significa que o arquivo de teste compila e liga independentemente do resto da base de código e executa em isolamento.

Quanto à compilação cruzada do código, para que isso funcione, você precisa de boas instalações no destino. Eu fiz isso com o googletest cross compilado para o Linux em uma arquitetura PowerPC. Isso faz sentido, porque você tem um shell e um completos para reunir seus resultados. Para ambientes menos ricos (que eu classifico como qualquer coisa sem um SO completo) você deve apenas construir e rodar no host. Você deve fazer isso de qualquer maneira para poder executar os testes automaticamente como parte da construção.

Eu acho que o teste de código C ++ é geralmente muito mais fácil devido ao fato de que o código OO é em geral muito menos acoplado do que processual (claro que isso depende muito do estilo de codificação). Também em C ++ você pode usar truques como injeção de dependência e substituição de métodos para obter costuras no código que é encapsulado de outra forma.

Michael Feathers tem um excelente livro sobre como testar código legado . Em um capítulo, ele aborda técnicas para lidar com código não OO, o que eu recomendo.

Edit : Eu escrevi uma postagem no blog sobre código procedural de teste unitário, com fonte disponível no GitHub .

Edit : Há um novo livro saindo dos programadores pragmáticos que aborda especificamente o código C de teste de unidade que eu recomendo .


Se você ainda estiver em busca de estruturas de teste, o CUnitWin32 é um para a plataforma Win32 / NT.

Isso resolve um problema fundamental que enfrentei com outras estruturas de teste. Ou seja, as variáveis ​​globais / estáticas estão em um estado determinístico porque cada teste é executado como um processo separado.


Se você está familiarizado com o JUnit, então eu recomendo o CppUnit. http://cppunit.sourceforge.net/cppunit-wiki

Isso é supondo que você tenha o compilador c ++ para fazer os testes unitários. se não, então eu tenho que concordar com Adam Rosenfield que cheque é o que você quer.


Uma técnica a ser usada é desenvolver o código de teste de unidade com uma estrutura C ++ xUnit (e o compilador C ++), mantendo a origem do sistema de destino como módulos C.

Certifique-se de compilar regularmente seu código-fonte C em seu compilador cruzado, automaticamente com os testes de unidade, se possível.


Você também pode querer dar uma olhada na libtap , uma estrutura de teste C que gera o TAP (Test Anything Protocol) e, portanto, integra-se bem com uma variedade de ferramentas que são lançadas para essa tecnologia. É usado principalmente no mundo da linguagem dinâmica, mas é fácil de usar e se tornar muito popular.

Um exemplo:

#include <tap.h>

int main () {
    plan(5);

    ok(3 == 3);
    is("fnord", "eek", "two different strings not that way?");
    ok(3 <= 8732, "%d <= %d", 3, 8732);
    like("fnord", "f(yes|no)r*[a-f]$");
    cmp_ok(3, ">=", 10);

    done_testing();
}




Minunit é uma estrutura de teste de unidade incrivelmente simples. Eu estou usando isso para teste de unidade c código do microcontrolador para avr.





embedded