TDD: Testando código C/C++ com Gcov

No post anterior (TDD: Testando código C/C++ com Boost.Test), explicamos como se cria uma suíte de testes com a biblioteca Boost.Test. Muito útil para testar a funcionalidade correta do código.

Neste post mostramos como utilizar o Gcov para verificar se todo o código é executado pelos testes. Criamos uma biblioteca simples e um conjunto de testes para ela.

Ferramentas de code coverage (cobertura de código) como Gcov são muito utilizados para “Desenvolvimento a partir de testes” (TDD – Test Driven Development).

No TDD desenvolvemos os códigos e seus testes em paralelo, para verificar que o código funciona e, no futuro, poder verificar se alguma mudança no código tenha causado um bug.

A análise da cobertura de código auxilia no TDD, identificando partes do código que não estão sendo testadas por completo.

Code coverage ou cobertura de código

Code coverage ou cobertura de código é definida como a porcentagem do código que coberto (executado) pelos testes. Podemos por exemplo ter um conjunto de testes que executa 90% das linhas de código.

Há duas métricas importantes para a cobertura de código: linhas executadas e condicionais executados.

Verificar se todas as linhas do código estão sendo executadas é importante. Se um bloco de código foi escrito, certamente ele serve para tratar alguma coisa. Se ele nunca é executado temos um dos seguintes problemas: os testes não cobrem esse caso ou é impossível de executar o bloco devido a um erro de programação.

Verificar se todos os condicionais do código são executados também é importante. Muitas vezes temos várias formas de executar um bloco, por exemplo, se ISSO1 for verdade ou ISSO2 for verdade… e para verificar se todos estes condicionais funcionam adequadamente devemos ter testes para cada um deles, um com ISSO1 sendo verdade e o outro com ISSO2 sendo verdade.

Instalando Gcov no Ubuntu Linux

Usamos o comando abaixo para realizar a instalação do G++ (compilador para C++), a ferramenta Gcovr e biblioteca Boost.Test no Ubuntu 18.04.

sudo apt install g++ gcovr libboost-test-dev

O Gcov por si próprio é parte do compilador G++.

A ferramenta Gcovr é utilizada para mostrar quais linhas ou condicionais não foram executados.

A biblioteca Boost.Test é utilizada para simplificar a criação de códigos de teste. Veja mais sobre Boost.Test no post TDD: Testando código C/C++ com Boost.Test.

Códigos de exemplo

Primeiro vamos criar os códigos de exemplo.

Segue o código de uma função simples, que retorna o valor absoluto de um inteiro.

// biblioteca.cpp
#include "biblioteca.h"

int absoluto(int val) {
  if (val >= 0)
    return val;
  else
    return -val;
}

Segue o cabeçalho correspondente a esta função.

// biblioteca.h
int absoluto(int val);

Segue um código de teste da função utilizando a biblioteca Boost.Test.

// teste.cpp
#define BOOST_TEST_MODULE TesteBiblioteca
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>

#include "biblioteca.h"

BOOST_AUTO_TEST_CASE(absouluto) {
  BOOST_TEST(absoluto(0) == 0);
  BOOST_TEST(absoluto(1) == 1);
  BOOST_TEST(absoluto(-1) == 1);
}

Compilando com Gcov

Queremos compilar apenas o código da biblioteca com informações do Gcov. Não queremos que o código de teste seja verificado com o Gcov. Por isso vamos compilar a biblioteca e os testes separadamente e depois linkar eles.

Para compilar a biblioteca com as informações do Gcov utilizamos o comando a seguir.

g++ --coverage -c -o biblioteca.o biblioteca.cpp

Para compilar os testes (sem Gcov) utilizamos o comando a seguir.

g++ -c -o teste.o teste.cpp

Para linkar os códigos-objeto compilados utilizamos o comando a seguir.

g++ --coverage -o teste biblioteca.o teste.o -lboost_unit_test_framework

Agora basta executar os testes.

./teste

Executando o Gcov

Executar o Gcov é muito simples. A opção -b adiciona as informações sobre os condicionais no programa (branches). Por exemplo, se seus testes sempre entram em um IF, ou nunca entram nele, isso será reportado.

gcov -b *.gcno
--
File 'biblioteca.cpp'
Lines executed:100.00% of 4
Branches executed:100.00% of 2
Taken at least once:100.00% of 2

Na saída vemos que o arquivo biblioteca.cpp teve 100% das suas linhas executadas (lines executed). Não apenas isso; também vemos que teve 100% dos condicionais foram executados (branches taken at least once).

No entanto, não temos informações sobre onde temos linhas não executadas ou condicionais não executados… Para isso utilizamos a ferramenta Gcovr.

Executando o Gcovr

A ferramenta Gcovr, que nos dá uma visualização melhor, em formato de tabela. No campo missing (faltando) temos uma lista das linhas que não foram executadas no teste.

gcovr
--
---------------------------------------
       GCC Code Coverage Report
Directory: .
---------------------------------------
File           Lines Exec Cover Missing
---------------------------------------
biblioteca.cpp 4     4    100% 
---------------------------------------
TOTAL          4     4    100%
---------------------------------------

Para ver as informações sobre os condicionais utilizamos a opção -b. No campo missing (faltando) temos uma lista das linhas com condicionais que não foram executados no teste.

gcovr -b
--
-------------------------------------------
        GCC Code Coverage Report
Directory: .
-------------------------------------------
File           Branches Taken Cover Missing
-------------------------------------------
biblioteca.cpp 2        2     100%   
-------------------------------------------
TOTAL          2        2     100%
-------------------------------------------

Um pequeno teste

Um teste simples para afiar sua mente quanto ao uso de ferramentas de cobertura de código.

  1. No arquivo teste.cpp, comente o terceiro teste. Recompile tudo e execute o teste.
    // BOOST_TEST(absoluto(-1) == 1);
    • Por que temos uma linha não executada?
    • Por que temos um condicional não executado?
  2. No arquivo teste.cpp, descomente o terceiro teste e comente o primeiro teste. Recompile tudo e execute o teste.
    // BOOST_TEST(absoluto(0) == 0);
    • Por que todas as linha estão sendo executadas?
    • Por que todos os condicionais estão sendo executados?

Quero mais!

Agora que já teve o pontapé inicial, é fácil prosseguir por conta:

Veja a documentação sobre a ferramenta GNU Gcov.

Até mais!

Leia mais:

Veja mais posts da Categoria Programação!

Autor: Djones Boni

Engenheiro Eletricista.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *