Testes automatizados: Como faço com o hardware?

Testes automatizados

Introdução

 

Sempre que vou discutir o uso de testes unitários em sistemas embarcados é recorrente a pergunta: Mas como você faz com o hardware?

 

Bem, a questão sempre aparece, é importante, mas no fim das contas a questão deveria ser: Quando inserir o hardware no processo de testes?

 

Para responder a essa questão temos de considerar primeiro a razão de existir daquele teste. A maior parte dos testes que escrevo são testes que existem para validar que a lógica da execução de uma parte do sistema está correta. Queremos portanto validar que para um dado cenário de execução da função, ou seja, para uma situação em que as variáveis das quais a função depende estejam em um valor especificado pelo teste, ações serão tomadas corretamente, e a saída da função será a esperada.

 

Em sistemas embarcados muitas vezes o problema reside na utilização de um periférico do microcontrolador que não está presente na máquina de desenvolvimento, então não é possível testar aquele código, certo? Errado!

 

Primeiro o objetivo do seu teste não deveria ser: O SPI está funcionando, mas sim, estou mandando as informações corretas ao SPI. O primeiro teste é sim relevante e só pode ocorrer com o uso do dispositivo e no hardware em questão. Entretanto, o segundo cenário é igualmente relevante e muito frequente.

 

 

Motivos para usar um substituto no seu teste

 

Antes de chegar ao uso das bibliotecas que pretendo apresentar neste texto, quero apontar alguns motivos principais para o uso de um substituto:

 

1 - É uma dependência que não faz parte do objetivo do meu teste.

 

Quando estamos escrevendo um teste para um dado subsistema, temos a tendência de querer chamar todas as suas dependências. Isso não é necessário em todos os estágios. Vamos considerar o exemplo de uma aplicação em IoT que faz uso de um modem 3G para a comunicação. Se vamos testar a montagem e envio das requisições precisamos do código referente ao modem, correto? Errado! O que precisamos é determinar como será a interface para o código cliente que utilizará o modem e definir o “contrato” de utilização. Naturalmente é importante ter testes que integrem as duas soluções de fato, mas esse é um outro teste em uma outra fase. Um passo de cada vez.

 

2 - É uma dependência que é impossível de ser integrada no ambiente de desenvolvimento.

 

Esse é o caso típico de quando vamos acessar um periférico em um microcontrolador. O endereço de memória em que ele está mapeado não pode ser acessado no ambiente de desenvolvimento pois não há o periférico por lá.

 

3 - Existe um tempo razoável para o uso do recurso.

 

Consideremos novamente a aplicação IoT que deve trocar mensagem com um sistema na nuvem. Pro seu código, que vai tratar a informação, não é relevante saber que a conexão foi fechada via TLS e o servidor respondeu em um determinado tempo. O que é relevante é: Enviei a requisição X, recebi a resposta Y e tomei a ação Z.

Resumindo e voltando a questão do objetivo do teste: Queremos validar o comportamento do código em teste.

 

Um caso de teste: Aguardando um semáforo e enviando informação via SPI.

Para mostrar situações que são típicas ao escrever testes em sistemas embarcados vamos tomar como exemplo uma aplicação que aguarda um semáforo para enviar via SPI um valor, e em caso de timeout envia um valor diferente. É um caso de teste simples mas nos permite propor algumas ideias:

  • Como lidar com o loop infinito que aparece nas funções que executam threads em RTOS?
  • Como controlar uma dependência que faz parte do meu teste para gerar casos de teste?
  • Como tratar o acesso ao hardware?

 

Uma pausa para escrita do teste: Catch

Para a escrita do teste vamos usar a ferramenta chamada Catch. Há outros frameworks de teste disponíveis e você pode experimentar para encontrar aquele que lhe agrade mais. O nosso primeiro passo é descrever o comportamento do nosso sistema.

 

 

Usando o Fake Function Framework

Para completar o nosso teste vamos utilizar uma biblioteca chamada Fake Function Framework (FFF). Com ela iremos substitur as funções de dependências no momento do link. Essa é uma boa técnica para substituir funções que atuam diretamente com o hardware e funções de sistema e dependências que não farão parte do nosso conjunto de testes. A implementação dessas funções não entrará na compilação do teste. E então proveremos uma implementação usando o FFF.

Para a implementação vamos supor a utilização do CMSIS-RTOSv2.

 

Observe no quadro anterior que o teste que descrevemos aguarda uma requisição de envio. Vamos então supor que a função é uma task em um RTOS e o que aguardaremos é a disponibilidade de um semáforo.

Vamos complementar o teste:

 

Alguns pontos a destacar:

  1. O início do teste é dedicado à construção do cenário, com estado das respostas e opções do teste;
  2. A função sob teste é exercitada como uma chamada de função normal dentro da estrutura do teste;
  3. Após a execução dos testes, os resultados são verificados com o auxílio dos substitutos construídos, usando o REQUIRE no nosso caso.

 

 

Conclusão

Executar testes em sistemas embarcados envolve várias etapas com diversos objetivos diferentes. Para o modelo de teste que apontei neste artigo, o que objetivamos é verificar se elementos da lógiga de negócios do sistema estão sendo respeitados. Validamos que a partir de uma entrada todas as dependências são chamadas na forma correta e um número certo de vezes, e assumimos o controle dessas dependências para validar que dado um certo cenário teremos a resposta correta do código implementado.

NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Euripedes Filho
Engenheiro eletricista pela esquecida Universidade Federal do Espírito Santo, atua no desenvolvimento de sistemas embarcados há quase 10 anos, é pai de 2 filhos, acredita que o Fluminense é o time mais importante pra formação da história do futebol do país. Almeja um dia dominar o mundo.

2
Deixe um comentário

avatar
 
2 Comment threads
0 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
2 Comment authors
Marcos De Lima CarlosCaio Pereira Recent comment authors
  Notificações  
recentes antigos mais votados
Notificar
Marcos De Lima Carlos
Visitante
Marcos De Lima Carlos

Show o artigo! Parabéns!

Caio Pereira
Visitante
Caio Pereira

Euripedes, que legal! Estava procurando algo semelhante duas semanas atrás. Acho que seu artigo vai nos ajudar. Obrigado e parabéns!