Testes unitários em C++ para um programador Java!

English version here
Eu trabalhei com C++ quando iniciei no mundo da programação (entre 1997 e 2000), mas na época eu trabalhava com o Borland C++ Builder e o Microsoft Visual C++, naquela época eu ainda não tinha ouvido falar em testes unitários, depois disto eu trabalhei com Delphi, PHO, ASP, ColdFusion, …
Desde 2002 eu trabalhei a maior parte do tempo com Java, e aprendi muito neste período, muitas boas práticas, muito sobre orientação a objetos e principalmente, aprendi a amar os testes unitários.
Pouco tempo atrás eu voltei a trabalhar com C++, mas já viciado em testes unitários, e querendo aplica-los ao meu código C++ também, e este post é um exemplo bem curto de como um programador Java pode trabalhar com C++ utilizando testes unitários.

Um projeto C++ começa por um Makefile, eu estou acostumado com o ANT e não gosto da idéia de listar todos os meus arquivos fonte na configuração de build como a maior parte dos exemplos de Makefiles fazem, então eu criei um Makefile simples, mas bastante flexível para o meu projeto.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
TESTDIRECTORIES := test
DIRECTORIES := src
SOURCES := $(foreach dir,$(DIRECTORIES),$(wildcard $(dir)/*.cpp))
TESTSOURCES := $(foreach dir,$(TESTDIRECTORIES),$(wildcard $(dir)/*.cpp))
OBJECTS := $(patsubst %.cpp,%.obj,$(SOURCES))
TESTOBJECTS := $(patsubst %.cpp,%.obj,$(TESTSOURCES))
TESTOBJECTS += $(filter-out src/main.obj,$(OBJECTS))
TARGET := example
LINK := g++
CC := g++
CFLAGS := -c
LFLAGS :=
 
all: $(OBJECTS)
	$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS)
 
test: $(TESTOBJECTS)
	$(LINK) $(LFLAGS) -lcppunit -o $(TARGET)_unit $(TESTOBJECTS) 
	./$(TARGET)_unit
 
%.obj:%.cpp
	$(CC) $(CFLAGS) -o $*.obj $*.cpp

Com este Makefile, todos os arquivos .cpp que estiverem no diretório src farão parte do executável gerado, mais diretórios podem ser adicionados simplesmente atualizando a variável DIRECTORIES, a mesma coisa acontece com o diretório test e a variável TESTDIRECTORIES para os testes unitários.
O truque aqui é a combinação das funções foreach e wildcard, a função pathsubst é usada para alterar as extensões de .cpp para .obj e a função filter-out é usada para remover o main.cpp dos testes unitários pois este arquivo é apenas o ponto de entrada para o executável principal.
Este Makefile é o mais próximo que eu consegui chegar da funcionalidade do ANT para programação C++, claro que ela pode ser melhorada, considerando que eu não sou um especialista em Makefiles.
Mas o Makefile não é o motivo deste post, estou escrevendo para contar para vocês sobre o CppUnit, uma ótima implementação xUnit para C++.
Eu comecei o projeto escrevendo o “executador de testes” do CppUnit:
testRunner.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <cppunit/CompilerOutputter.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TestRunner.h>
#include <cppunit/BriefTestProgressListener.h>
 
int main (int argc, char* argv[])
{
    // informs test-listener about testresults
    CPPUNIT_NS :: TestResult testresult;
 
    // register listener for collecting the test-results
    CPPUNIT_NS :: TestResultCollector collectedresults;
    testresult.addListener (&collectedresults);
 
    // register listener for per-test progress output
    CPPUNIT_NS :: BriefTestProgressListener progress;
    testresult.addListener (&progress);
 
    // insert test-suite at test-runner by registry
    CPPUNIT_NS :: TestRunner testrunner;
    testrunner.addTest (CPPUNIT_NS :: TestFactoryRegistry :: getRegistry ().makeTest ());
    testrunner.run (testresult);
 
    // output results in compiler-format
    CPPUNIT_NS :: CompilerOutputter compileroutputter (&collectedresults, std::cerr);
    compileroutputter.write ();
 
    // return 0 if tests were successful
    return collectedresults.wasSuccessful () ? 0 : 1;
}

CppUnit é mito flexível, permitindo diversos tipos de saída para os resultados dos testes, mas escreverei sobre isto em outro post, a idéia atrás deste “executador” é a utilização do registro de testes do CppUnit, o que torna a vida muito mais fácil.
O registro de testes é bem próximo ao fileset passado a task junit do ant, mas os testes se registram sozinhos.
Depois do “executador de testes” pronto, podemos começar a escrever os testes unitários.
Em C++ diferente do Java, são necessários dois arquivos para cada classe, um cabeçalho e uma implementação.
Então, vamos começar com o cabeçalho.
mainTest.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef MAINTEST_H
#define MAINTEST_H
 
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include "../src/HelloWorld.hpp"
 
using namespace std;
 
class MainTest : public CPPUNIT_NS :: TestFixture
{
    CPPUNIT_TEST_SUITE (MainTest);
    CPPUNIT_TEST (testHello);
    CPPUNIT_TEST_SUITE_END ();
 
    public:
        void setUp (void);
        void tearDown (void);
        void testHello (void);
    private:
        HelloWorld *hello;
};
CPPUNIT_TEST_SUITE_REGISTRATION (MainTest);
#endif

Neste cabeçalho temos uma declaração de classe simples, extendendo TestFixture do namespace do CppUnit.
C++ não possui reflexão, por isto o CppUnit possui algumas macros para definir o teste, que podem ser vistas no início da declaração da classe, será necessária uma linha com CPPUNIT_TEST para cada método de teste que você declarar.
A linha: CPPUNIT_TEST_SUITE_REGISTRATION (MainTest);
Faz a mágica do auto registro dos testes.
Com isto pronto, você pode começar a implementar a classe de testes como faria em java:
mainTest.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "mainTest.hpp"
 
void MainTest::setUp(){ 
	hello = new HelloWorld("Test");
}
 
void MainTest::tearDown(){
	delete hello;
}
 
void MainTest::testHello(){
	string expected("Hello Test\n");
	CPPUNIT_ASSERT_EQUAL(expected,hello->sayHello());
}

Como no JUnit existem os métodos setUp e tearDown que são executados antes e depois de cada um dos testes, e o método testHello possui o código do teste (ja que só foi implementado um para este exemplo).
As asserções no CppUnit são feitas utilizando macros.
O CppUnit disponibiliza as seguintes asserções:

  • CPPUNIT_ASSERT(condition)
  • CPPUNIT_ASSERT_MESSAGE(message,condition)
  • CPPUNIT_FAIL( message )
  • CPPUNIT_ASSERT_EQUAL(expected,actual)
  • CPPUNIT_ASSERT_EQUAL_MESSAGE(message,expected,actual)
  • CPPUNIT_ASSERT_DOUBLES_EQUAL(expected,actual,delta)
  • CPPUNIT_ASSERT_THROW( expression, ExceptionType )

Muito menos do que no JUnit, mas o suficiente para a grande maioria dos casos.
Depois do teste pronto, agora precisamos escrever o código para que os testes passem.
HelloWord.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <iostream>
 
#ifndef MAIN_HPP
#define MAINHPP
class HelloWorld{
private:
	std::string name;
public:
	HelloWorld(char* name);
	std::string sayHello();
};
#endif

E a implementação:
HelloWord.cpp

1
2
3
4
5
6
7
8
9
10
11
#include "HelloWorld.hpp"
 
HelloWorld::HelloWorld(char* name){
	this->name = name;
}
 
std::string HelloWorld::sayHello(){
	std::string result("Hello ");
	result = result + name + "\n";
	return result;
}

Para executar os testes, basta você executar no console:
make test
Agora que todos os testes foram escritos e estão passando, o último passo é escrever o código para inicializar a aplicação:
main.cpp

1
2
3
4
5
6
7
8
#include "HelloWorld.hpp"
int main(int argc, char** argv){
	if (argc >= 2) {
		HelloWorld* hello = new HelloWorld(argv[1]);
		std::cout << hello->sayHello();
		delete hello;
	}
}

E você tem a sua primeira aplicação test driven escrita em C++!
PS.: se você esta utilizando o Makefile que escrevi, lembre que o código da aplicação deve ficar no diretório src e o código de testes no diretório test.
PS2.: O exemplo foi testado em um linux com o CppUnit instalado pelo gerenciador de pacotes, se você quiser instalar o cppunit usando o código fonte ou for executar em outra plataforma lembre-se de atualizar o CFLAGS com os caminhos de influde corretos e o LFLAGS com o caminho da biblioteca do cppunit, se você não esta utilizando o g++ como compilador e linker, lembre-se de atualizar as variáveis CC e LINK.

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

RSpec Stories - Ruby gerado a partir do texto!

Não, eu não quero que vocês gerem o código dos testes, isto iria apenas criar testes inúteis!
Mas eu acho muito chato ter certeza de que todos os possíveis passos […] Continue Reading…

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Como implementar paginação para listboxes com muitos elementos com o ZK Framework

Segue mais um post sobre o ZK Framework escrito pelo Marcos de Sousa.
Muito bom o post, e o ZK é bem legal tanbém, para saber mais sobre o ZK […] Continue Reading…

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Programa de afiliados da 37signals

A 37signals é o ambiente de trabalho dos sonhos de muita gente (logo depois do google :D ), eles desenvolveram alguns produtos bem legais que ajudam muita gente, todos […] Continue Reading…

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

O JavaPlanet reencarnou!

Vocês lembram do site que eu coloquei no ar a algum tempo atrás, o JavaPlanet que era um agregador de feeds de sites sobre java? um pouco antes de […] Continue Reading…

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Orientação a objetos é fácil, as pessoas é que complicam

A idéia de escrever este post veio desta discussão no GUJ, que começou falando de testes unitários e terminou sobre orientação a objetos.
Então seguem as minhas opiniões.

As linguagens de […] Continue Reading…

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

mod_rails revolucionando as hospedagens Rails compartilhadas, inclusive no Brasil

Já existem alguns provedores internacionais disponibilizando o mod_rails como opção para hosting compartilhado de aplicações Ruby On Rails, como por examplo o HostingRails.com (se tiverem mais para adicionar a […] Continue Reading…

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Test Infected to the bones - tornando os testes mais divertidos

Uma das coisas mais difíceis em introduzir a idéia do TDD é fazer com que os outros programadores da equipe realmente utilizem os testes, e para tornar esta árdua […] Continue Reading…

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

1o JUGDAY do RSJUG

O RSJUG estara realizando no final deste mês mais um evento aqui em porto alegre, segue abaixo o anuncio do evento, vale a pena participar se vocês estiverem por […] Continue Reading…

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

*nix history Meme

Tudo bem, pode ser a completa e total falta do que fazer, mas vamos ver se o pessoal resolve entrar na brincadeira :D
A idéia é listar os comandos mais […] Continue Reading…

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Autocomplete para rails com jquery muito fácil!

No último projeto que estou trabalhando, resolvi utilizar jQuery em vez do prototype padrão …

E eu precisava de um campo de texto com auto completar, e mesmo que estivesse […] Continue Reading…

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Layout novo!

Bom, o layout antigo, aquele parecido com um Mac ja estava todo empoeirado, cheirando a mofo :D
então resolvi mudar o layout do blog.
Deem uma olhada e me digam o […] Continue Reading…

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Plugins para rails e grails

Publiquei eles só no meu blog em ingles, mas acho que vale o link aqui pelo menos …

Para quem é tão preguiçoso quanto eu, eu criei um plugin para […] Continue Reading…

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Grails - Agilidade, produtividade e código bonito ao seu alcance

Bom, este foi o titulo da minha palestra no FISL este ano :D
E como eu prometi, este post é para disponibilizar o conteúdo da palestra e o código do […] Continue Reading…

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Mais um tutorial do RSJUG - Clusterização e Load Balance de aplicações JEE

Desta vez o tutorial vai ser sobre Clusterização e Load Balance de aplicações JEE
Segue um quote no anuncio na página do JUG:

Tutorial Clusterização e Load Balance de aplicações JEE

Neste […] Continue Reading…

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!