Projetos de softwares são complexos porque eles resolvem problemas de alta complexidade. Certamente, muitos sistemas são feitos apenas com um CRUD básico e essa é uma solução perfeitamente aceitável para solucionar problemas mais simples do cliente. Entretanto, conforme um sistema cresce, é inevitável que os sistemas precise tratar casos de usos mais elaborados e que exigem uma modelagem mais adequada para refletir essas regras de negócios.
Na prática, é muito difícil que todos os desenvolvedores conheçam todos os detalhes das regras mesmo se ele tivesse contato direto com os experts do negócio. Por isso, muitas das alterações propostas pelos devs podem (e vão) causar regressões no sistemas. Uma equipe de QA poderia identificar esses problemas durante a etapa de testes. Todavia, a forma mais eficiente de evitar esse problema é manter uma base sólida de testes unitários que cubram a maioria das funcionalidades principais do sistema. Assim, o dev se sentirá mais seguro para realizar as alterações sem o risco de causar bugs.
Em C++, o Google Test é dos Frameworks mais utilizados para a implementação de testes automatizados. Neste tutorial, vamos aprender a configurá-lo e fazer nosso primeiro teste unitário.
Primeiramente, vamos escrever a nossa função que será testada. Temos uma classe Aluno com a informação da nota média no ano de 0 a 10. Temos uma classe Escola que contém a lista de alunos. Queremos saber quais alunos foram reprovados, ou seja, tiveram nota menor do que 5.
includes/student.h
#ifndef INCLUDED_STUDENT
#define INCLUDED_STUDENT
#include <string>
class Student {
public:
Student(std::string name, float grade)
: name(name), grade(grade) {};
std::string name;
float grade;
std::string output() const;
bool isApproved() const;
};
#endif
includes/school.h
#ifndef SCHOOL_H
#define SCHOOL_H
#include <vector>
#include <string>
#include "student.h"
class School {
public:
void addStudent(const Student& student);
std::vector<Student> getNotApprovedStudents() const;
private:
std::vector<Student> students;
};
#endif
src/student.cpp
#include "student.h"
std::string Student::output() const {
return name + " scored a " + std::to_string(grade);
}
bool Student::isApproved() const {
return grade >= 5.0;
}
src/school.cpp
#include "school.h"
#include <algorithm>
#include <vector>
void School::addStudent(const Student& student) {
students.push_back(student);
}
std::vector<Student> School::getNotApprovedStudents() const {
std::vector<Student> result;
std::copy_if(
students.begin(),
students.end(),
std::back_inserter(result),
[](const Student& student) { return !student.isApproved();}
);
return result;
}
Vamos iniciar a instalação do Google Test. Para isso, é necessário clonar o repositório e instalar a biblioteca. Utilize os seguintes comandos para isso:
git clone https://github.com/google/googletest.git -b v1.16.0
cd googletest
mkdir build
cd build
cmake ..
make
sudo make install
Após a instalação, podemos criar nosso primeiro teste. Queremos verificar se o sistema retorna corretamente a lista de alunos não aprovados:
tests/school_test.cpp
#include <gtest/gtest.h>
#include "school.h"
TEST(SchoolTest, studentsNotApproved) {
School school;
school.addStudent(Student("João", 7.0));
school.addStudent(Student("Maria", 10.0));
school.addStudent(Student("Pedro", 5.0));
school.addStudent(Student("Lucas", 8.0));
school.addStudent(Student("Carolina", 3.0));
school.addStudent(Student("Mateus", 4.0));
school.addStudent(Student("Carlos", 7.0));
std::vector<Student> studentsNotApproved = school.getNotApprovedStudents();
EXPECT_EQ(studentsNotApproved.size(), 2);
EXPECT_TRUE(std::any_of(
studentsNotApproved.begin(),
studentsNotApproved.end(),
[](Student student){return student.name == "Carolina";})
);
EXPECT_TRUE(std::any_of(
studentsNotApproved.begin(),
studentsNotApproved.end(),
[](Student student){return student.name == "Mateus";})
);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Por fim, podemos executar os testes. Execute os seguintes comandos:
mkdir -p build
g++ -std=c++17 -o build/test.out tests/school_test.cpp src/student.cpp src/school.cpp -Iincludes -lgtest -lgtest_main -pthread
./build/test.out
Você deve ver o seguinte resultado no terminal:
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from SchoolTest
[ RUN ] SchoolTest.studentsNotApproved
[ OK ] SchoolTest.studentsNotApproved (0 ms)
[----------] 1 test from SchoolTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 1 test.
Com os testes concluídos, podemos testar o código e confirmar o resultado:
src/main.cpp
#include <student.h>
#include <school.h>
#include <vector>
#include <iostream>
#include <algorithm>
int main()
{
std::vector<Student> students;
School school;
school.addStudent(Student("João", 7.0));
school.addStudent(Student("Maria", 10.0));
school.addStudent(Student("Pedro", 5.0));
school.addStudent(Student("Lucas", 8.0));
school.addStudent(Student("Carolina", 3.0));
school.addStudent(Student("Mateus", 4.0));
school.addStudent(Student("Carlos", 7.0));
students = school.getNotApprovedStudents();
for (const auto& student : students) {
std::cout << student.output() << std::endl;
}
return 0;
}
Compilado o código e executando o programa com o comando:
mkdir -p build
g++ -std=c++17 -o build/main.out src/main.cpp src/student.cpp src/school.cpp -Iincludes
./build/main.out
Temos a seguinte saída:
./build/main.out
Carolina scored a 3.000000
Mateus scored a 4.000000
Conclusão
Com isso, terminamos nosso primeiro teste no Google Test. Recomendo que sempre mantenha as funcionalidades principais do seu sistema com a quantidade adequada de testes unitário para trazer segurança para a sua equipe. Em especial, se atente sempre aos corner cases, pois esses são os piores cenários e que podem não ser testados extensivamente pelos testadores.
O exemplo completo pode ser encontrado em https://github.com/christianmiyoshi/TutorialGoogleTest. Para executar, recomendo criar um codespace no github, instalar o Google Test e executar os comandos make test para validar os testes. O programa principal pode ser executado com o make run.