Bons exemplos de teste de unidade para desenvolvedores C incorporados [fechado]

20

Vou falar com o meu departamento na próxima semana sobre testes de unidade e desenvolvimento orientado a testes. Como parte disso, vou mostrar alguns exemplos do mundo real de algum código que escrevi recentemente, mas também gostaria de mostrar alguns exemplos muito simples que vou escrever na palestra.

Eu tenho pesquisado na web por bons exemplos, mas tenho lutado para encontrar algum que seja particularmente aplicável à nossa área de desenvolvimento. Quase todo o software que escrevemos são sistemas de controle profundamente embutidos, rodando em pequenos microcontroladores. Há muito código C que é facilmente aplicável ao teste de unidade (falarei sobre o teste de unidade no PC, e não no próprio alvo), desde que você fique longe da camada 'inferior': o que fala diretamente aos periféricos do microcontrolador. No entanto, muitos exemplos que encontrei tendem a se basear no processamento de strings (por exemplo, o excelente exemplo de numeração romana Dive Into Python) e, como quase nunca usamos strings, isso não é realmente adequado (sobre as únicas funções de biblioteca que nosso código normalmente usa são memcpy, memcmpe memset,strcat ou expressões regulares não estão certas).

Então, sobre a questão: alguém pode oferecer bons exemplos de funções que eu possa usar para demonstrar o teste de unidade em uma sessão ao vivo? Uma boa resposta na minha opinião (sujeita a alterações) provavelmente seria:

  • Uma função que é simples o suficiente para que qualquer pessoa (mesmo aqueles que escrevem código ocasionalmente) possa entender;
  • Uma função que não parece inútil (ou seja, calcular a paridade ou CRC é provavelmente melhor do que uma função que multiplica dois números e adiciona uma constante aleatória);
  • Uma função que é curta o suficiente para escrever na frente de uma sala de pessoas (eu posso tirar proveito das muitas pranchetas do Vim para reduzir erros ...);
  • Uma função que aceita números, matrizes, ponteiros ou estruturas como parâmetros e retorna algo semelhante, em vez de manipular strings;
  • Uma função que possui um erro simples (por exemplo, em >vez de >=) que é fácil de inserir e que ainda funcionaria na maioria dos casos, mas que rompe com um caso particular: fácil de identificar e corrigir com um teste de unidade.

Alguma ideia?

Embora provavelmente não seja relevante, os próprios testes provavelmente serão escritos em C ++ usando o Google Test Framework: todos os nossos cabeçalhos já possuem o #ifdef __cplusplus extern "C" {wrapper; isso funcionou bem com os testes que fiz até agora.

DrAl
fonte
Tomando o "problema" aqui como uma apresentação para vender o TDD à gerência, isso me parece razoavelmente adequado ao formato desejado. O OP parece estar solicitando soluções existentes para esse problema.
Technophile 11/01

Respostas:

15

Aqui está uma função simples que deve gerar uma soma de verificação sobre bytes len .

int checksum(void *p, int len)
{
    int accum = 0;
    unsigned char* pp = (unsigned char*)p;
    int i;
    for (i = 0; i <= len; i++)
    {
        accum += *pp++;
    }
    return accum;
}

Ele tem um bug de cerca: na declaração for, o teste deve ser i < len.

O engraçado é que, se você aplicá-lo a uma sequência de texto como esta ...

char *myString = "foo";
int testval = checksum(myString, strlen(myString));

você receberá a "resposta certa"! Isso ocorre porque o byte extra que foi soma de verificação foi o terminador de seqüência zero. Portanto, você pode colocar essa função de soma de verificação em código e talvez até enviá-la, e nunca perceber um problema - ou seja, até começar a aplicá-la a algo diferente de cadeias de texto.

Aqui está um teste de unidade simples que sinalizará esse bug (na maioria das vezes ... :-)

void main()
{
    // Seed the random number generator
    srand(time(NULL));

    // Fill an array with junk bytes
    char buf[1024];
    int i;
    for (i = 0; i < 1024; i++)
    {
        buf[i] = (char)rand();
    }

    // Put the numbers 0-9 in the first ten bytes
    for (i = 0; i <= 9; i++)
    {
        buf[i] = i;
    }

    // Now, the unit test. The sum of 0 to 9 should
    // be 45. But if buf[10] isn't 0 - which it won't be,
    // 255/256 of the time - this will fail.
    int testval = checksum(buf, 10);
    if (testval == 45)
    {
        printf("Passed!\n");
    }
    else
    {
        printf("Failed! Expected 45, got %d\n", testval);
    }
}
Bob Murphy
fonte
Muito bom! Este é exatamente o tipo de resposta que eu esperava: obrigado.
Dral
Quando você cria o buffer, você já tem lixo nesse pedaço de memória, é realmente necessário inicializá-lo com números aleatórios?
Cobra Sanders
@SnakeSanders, eu diria que sim, porque você deseja que os testes de unidade sejam o mais deterministas possível. Se o compilador usado colocar um 0 na máquina do desenvolvedor e um 10 na máquina de teste, você terá um tempo terrível para encontrar o bug. Eu acho que fazê-lo depender do tempo em vez de uma semente fixa é uma má idéia, pelo mesmo motivo.
Andrew diz Reinstate Monica
Confiar em comportamentos não determinísticos em um teste de unidade é uma má ideia. Um teste escamosa vai lhe dar dores de cabeça, mais cedo ou mais tarde ...
sigy
2

Que tal implementar uma função de classificação como a classificação por bolhas ? Depois que a função de classificação estiver funcionando, você poderá continuar com a pesquisa binária, que é igualmente boa para introduzir testes de unidade e TDD.

A classificação e a pesquisa dependem de comparações que são fáceis de errar. Também envolve a troca de indicadores em torno dos quais deve ser feito com cuidado. Ambos são propensos a erros, então fique à vontade para estragar :)

Mais algumas idéias:

  • Os testes de unidade ajudam muito ao refatorar. Portanto, assim que sua classificação por bolhas funcionar, você poderá alterá-la para uma classificação mais poderosa qsort, e os testes ainda deverão passar, provando que sua nova função de classificação também funciona.
  • A classificação é fácil de testar, o resultado é classificado ou não, o que o torna um bom candidato.
  • O mesmo para pesquisar; existe ou não existe.
  • Escrever testes para classificação abre discussões como que tipo de entrada usar para teste (zero elementos, entrada aleatória, entradas duplicadas, matrizes enormes, etc.).
Martin Wickman
fonte
Você tem alguma sugestão específica para um erro simples que mostraria como o teste facilita a vida?
DrAl 26/05
@ DrAl: Atualizei minha resposta com isso.
Martin Wickman