top of page
Foto do escritorUFRJ Nautilus

Como Fazer um Teste Unitário com ROS?

O que são testes unitários?


Antes de tudo, é importante entender o que são testes unitários. De forma geral, um projeto software é dividido em diversas funções interdependentes. Inicialmente em um projeto é fácil se certificar que todas funcionam corretamente, porém na medida que um projeto se expande, e novas funções são adicionadas, essa certificação de que tudo funciona corretamente, mesmo com a adição de novas funcionalidades é essencial. E se criássemos códigos para otimizar essa verificação? Isso é o modelo de unit testing ou teste unitário.

Este método consiste em testar blocos de código de forma separada para que se tenha certeza de que a lógica escrita esteja correta e possa ser utilizada em outras partes do código.


Por que fazer?


Os teste unitários são úteis para agilizar o processo de desenvolvimento. Quando há uma testagem constante é mais fácil encontrar bugs no código e, assim, os desenvolvedores conseguem focar em ampliar o software ao invés de ficarem presos o concertando.

Outro fator que se deve ressaltar é que, muitas vezes, o próprio programador que escreverá blocos de código criará também os testes unitários para este bloco.

Com isso, tem-se um trabalho levemente maior para desenvolver o código, porém os benefícios a longo prazo são grandes o suficiente para compensar essa demora momentânea.

Agora que já sabemos o que é um teste unitário, podemos começar.


Hands-on


Usaremos o turtlebot3 no simulador Gazebo para explicarmos como podemos implementar testes unitários em projetos do ROS. O Turtlebot é uma plataforma padrão do ROS, muito usada para fins educacionais.


Olhe o código abaixo:

Esse é um código razoavelmente complexo, e talvez fique um pouco difícil de entender o que está acontecendo, mas é bastante simples. Esse script implementa um controlador PID para mover o turtlebot até um certo ponto pré definido.

Agora que entendemos o que é feito, podemos pensar em como testar isso para verificar se está correto.

Um teste interessante é se quando botamos para rodar o programa o turtlebot realmente sabe onde é o objetivo final, no nosso código, usamos um servidor de parâmetros para dizer para o turtlebot o objetivo final. Podemos, então, verificar se esses parâmetros estão sendo carregados corretamente.

Definimos a classe TestTurtlebot como subclasse da unittest.TestCase, isso é essencial para que seja possível testar as funções separadamente utilizando o framework unittest. Além disso, também por conta do unittest, as funções que serão executadas na testagem devem possuir test_ como prefixo. Caso esteja utilizando frameworks diferentes veja a documentação do framework utilizado.

Quanto a função test_param_server, estamos utilizando a função do rospy.get_param() para retornar o valor de cada um desses tópicos. Caso não existam, a função retornará None. Depois testamos essas variáveis locais e verificamos se é ou não é None.

É um teste muito simples, porém extremamente importante. A verificação dos parâmetros é cada vez mais necessária na medida que o projeto avança.

Ok, já temos um teste pronto! Mas, pense comigo, qual a utilidade de saber exatamente onde está o objetivo se o turtlebot não consegue alcançá-lo? Vamos criar um teste pra saber se o turtlebot realmente está chegando em seu objetivo.

Neste segundo teste, utilizando os valores que nos certificamos que foram carregados no servidor de parâmetros, faremos uma comparação de valores de (x,y) iniciais e finais com intuito de nos certificar da eficácia no movimento do turtlebot. Além disso, temos também uma margem de erro no servidor de parâmetros e devemos levá-la em consideração durante a testagem.

Nesse teste vemos também a interdependência dos testes, visto que se o primeiro teste falhar, esse também falhará. Do mesmo modo, após o consertar um teste que está falhando acarreta possíveis benefícios para outros testes

Vale ressaltar que testes não são estáticos após sua criação. Com o desenvolvimento de funcionalidades mais complexas, testes anteriores podem ser aperfeiçoados. Nesse caso, levar em consideração um tempo máximo para o turtlebot demora para chegar no objetivo pode ser uma otimização futura.

Por último, vamos criar um teste para verificar se a comunicação do sistema internamente está correta. No código move2goal_turtlebot.py vemos que o turtlebot publica sua velocidade no tópico /cmd_vel pelo tipo de mensagem Twist.

Vamos criar um teste que verifica se há mensagem sendo enviada nesse tópico durante a execução do programa.

No código podemos ver que a função velocityCallback não possui test_ como prefixo então ela não será executada de forma isolada durante a testagem. A ideia dessa função é criar um ROS Subscriber que estará conectado a /cmd_vel esperando uma mensagem. Caso essa mensagem não seja escutada, dentro de um intervalo de 5 segundos o código não entrará na função velocityCallback e a variável IsPublishing se manterá como False. No final fazemos a verificação do valor dessa variável.

Ok! Criamos vários testes interessantes… E agora? Como utilizar esses testes de maneira produtiva? Para isso, nós utilizamos as launch files, arquivos que auxiliam na automatização de diversas tarefas com ROS e que merecem um artigo próprio.


Caso queira aprender mais sobre launch files ou ROS em geral:


Caso queira conhecer a plataforma usada:



Escrito por: Matheus Rodrigues e Arthur Sobrinho

0 comentário

Posts recentes

Ver tudo

Comments


bottom of page