Primeiras aulas do curso C++ e TDD: testes de unidade com Catch2

C++ e TDD: testes de unidade com Catch2

Por que testar? - Apresentação

Nesse treinamento vamos começar falando de uma aplicação de leilão. Vamos ter lances, usuários e o leilão propriamente dito. Na hora de avaliarmos leilões nós vamos ver a necessidade de criarmos testes para isso, vamos entender o motivo de ter testes automatizados, vamos ver o que é um teste automatizado e o que ele precisa ter para ser considerado um teste automatizado.

Além disso tudo, obviamente, vamos utilizar ferramentas profissionais de testes automatizados. Aqui vamos utilizar uma conhecida como Catch 2, vamos ver como configurá-lo, como instalá-lo e vamos ver que é muito simples.

Nesse processo vamos aprender coisas como classes de equivalência, vamos organizar nossos testes e vamos otimizar o tempo de compilação dos nossos testes. Vamos aprender sobre TDD (Test Driven Development). Vamos aprender bastante coisas durante esse treinamento!

Acho válido citar que durante esse treinamento vamos focar em um tipo de teste, conhecido como Teste de Unidade. Isso quer dizer de forma resumida que um teste de unidade é aquele teste que verifica o funcionamento da menor unidade possível do código. Por exemplo: uma função, um método, uma classe, o menor possível para conseguir executar algo funcional e verificar a saída daquilo, verificar o comportamento.

Teste de unidade é o tipo de teste, segundo o que podemos ver nessa pirâmide, é o tipo de teste que mais devemos ter em um projeto real. Porque o teste de unidade é rápido e é confiável, nós sabemos que ele não muda tanto conforme o sistema cresce.

Já um teste de integração depende de integração com outras coisas. Por exemplo: o seu programa chama um outro executável, salvo em um arquivo. Se esse executável não estiver disponível, o seu teste falha; se o arquivo tentar ser escrito em um momento que você não tem permissão, o teste falha. Ele é mais propenso à falha, além de ser mais lento.

E testes end-to-end são testes que realizam as verificações pela perspectiva do usuário. Se é um teste de um jogo, você vai abrir a tela do jogo e realizar alguns comandos; se é um editor de texto, por exemplo, você vai abrir a janela do editor de texto e clicar em alguns botões. Se um botão muda de lugar todo o teste quebra. Esse tipo de teste é mais lento e mais propenso à falha.

Se você conhece o desenho do papa-léguas e coiote, você entendeu essa analogia, basicamente é isso. Em um cenário real você deve ter muitos testes de unidade, alguns testes de integração e só deve ter testes ponta a ponta para o cenário mais crítico da sua aplicação.

Nesse treinamento nós vamos focar em teste de unidade, vamos aprender técnicas de teste e ferramentas; enfim, tudo isso que eu já falei. Te espero no próximo vídeo para você conhecer a aplicação que vamos testar!

Por que testar? - Conhecendo o projeto

Boas-vindas de volta! Vamos conhecer o ambiente, o sistema em que vamos trabalhar.

Eu disponibilizei para vocês, no primeiro exercício desse treinamento, o arquivo que vai ter todas as classes desse projeto. Você só precisa criar um projeto novo na sua IDE, seja lá qual IDE você estiver utilizando, e importar esses arquivos como já fizemos em treinamentos anteriores.

Vamos dar uma olhada aqui no que temos. Primeiro, temos uma classe que representa um usuário. O usuário é quem vai dá um lance no leilão, no nosso sistema de leilão. Por enquanto ele só tem o nome; se alguma coisa a mais for necessária, posteriormente adicionaremos.

Aqui é a implementação do usuário, é só a inicialização do construtor e um getter, nada além disso.

E temos a representação de um lance. Um lance tem o usuário que deu esse lance e o valor do lance em si. Tudo o que precisamos são essas informações. De novo, se precisarmos de alguma a mais, no futuro nós veremos.

A implementação do lance só tem a inicialização dos seus valores e um getter do valor. Você pode me falar: "Vinicius, não tem nenhum getter do usuário aí." Se precisarmos desses getter, aí implementaremos - provavelmente vamos precisar. Enquanto ele não for necessário não tem motivo para precisarmos dele.

Agora vamos olhar o que representa um leilão em si. O leilão tem uma descrição, ou seja, esse leilão é referente a quê? E os lances neste leilão? Um leilão pode receber um lance ou vários lances. Estamos representando aqui como um vector, que também já aprendemos em treinamentos anteriores, inclusive com templates etc.

A partir disso, vamos dar uma olhada na implementação antes. Temos a inicialização da descrição no construtor e reparamos que o vector não está sendo inicializado aqui porque o vector começa vazio mesmo. Não recebemos nada por parâmetro.

Quando colocamos essa linha aqui, na hora que um leilão for criado isso aqui vai criar uma nova instância de um vector de lances. Isso já vai ter um vector vazio e vamos ter um leilão sem lances inicialmente. Não precisamos inicializar nada.

Temos aqui umgetter para recuperar os lances. Repare que eu estou devolvendo uma referência constante. Isso quer dizer que eu não estou realizando uma cópia do que vai ser retornado, eu não preciso disso; mas estou retornando constante para que ninguém possa alterar, caso eu receba esse valor. Claro que existem formas de burlarmos isso, mas não vamos entrar nesses detalhes agora.

Um leilão pode receber um lance, recebemos uma referência para um lance aqui e adicionamos esse lance lá no nosso vetor. Essa é a ideia por trás do nosso sistema, é isso que temos até agora. Durante esse treinamento, a ideia é implementarmos funcionalidades novas e em cima dessas funcionalidades realizarmos testes e automatizarmos os possíveis casos de testes.

A partir do próximo vídeo nós vamos começar a implementar funcionalidades novas, organizar melhor as nossas coisas. Enfim, no próximo vídeo começaremos a efetivamente colocar a mão na massa!

Por que testar? - Avaliando um leilão

Vamos criar agora uma classe que vai realizar a avaliação de um leilão. A princípio essa classe vai avaliar um leilão e nos retornar o maior o lance disponível, o maior lance que foi dado nesse leilão.

Vamos criar aqui uma nova classe. Vou em "New File...", vou criar um arquivo C++, vou adicionar um arquivo de cabeçalho também e vou chamar de Avaliador. Essa classe de avaliador vai ficar aqui mesmo junto com todos os outros na mesma pasta.

Você provavelmente já percebeu que estou utilizando um sistema diferente dos treinamentos anteriores. Nessa IDE, no Xcode, o modelo que ele cria para nós é diferente. Ao invés de utilizarmos o Pragma para termos o arquivo de cabeçalho, ele utiliza essa forma que é um pouco mais antiga, mas ainda muito comum também.

Vou manter isso para nos habituarmos e vou definir aqui a nossa classe Avaliador: class Avaliador. Na definição dessa classe eu preciso ter um método avalia. Esse método não vai devolver nada, ele vai avaliar um leilão. Obviamente, a minha IDE não conhece esse tipo; se eu salvar aqui, ela vai dar um erro. Eu preciso incluir o cabeçalho de leilão: #include Leilao.hpp.

Agora eu tenho um método que não devolve nada e avalia um leilão: class Avaliador { public: void avalia(Leilao};. Quando esse leilão for avaliado, algumas informações serão preenchidas aqui na minha classe.

Algumas informações como o que já comentamos, o maior valor ou o valor mais alto, como você preferir. Vai ser o maior valor de lance que vamos receber nesse leilão: private: float maiorValor;

Basicamente, é isso que precisamos por agora. Vamos para a implementação. Eu vou apagar todos esses comentários. Primeiro, um void Avaliador::avalia(Leilao);. Talvez você ainda não tenha se habituado com essa ideia, já falamos em treinamentos anteriores. Se você não estiver habituado com a ideia de não colocar o nome da variável aqui, o nome do parâmetro. Isso é perfeitamente válido.

Como aqui o leilão só seria repetido, não faz sentido adicionarmos, não vai adicionar valor nenhum para essa definição. Já na implementação nós precisamos sim de um nome, senão a sintaxe vai ser incorreta.

Vamos continuar! Para avaliar um leilão eu preciso realizar algumas tarefas. Primeiro: pegar o seu maior valor. Para pegar o maior valor eu posso pegar o último valor que foi dado nesse leilão, porque em um leilão alguém coloca um lance e outra pessoa dá um lance maior e outro um lance maior. Eu vou receber o último lance que foi dado nesse leilão.

A primeira coisa que vou fazer vai ser pegar um vetor de lance, que vão ser os lances desse leilão: std::vector<Lance> lances = leilao.recuperaLances();. Peguei os lances.

Agora eu quero pegar o último lance que vai ser desse vector temos acesso ao método back, que vai dar o último elemento deste vetor: Lance ultimoLance = lances.back();

Um detalhe importante é que se esse vetor estiver vazio aqui, o comportamento vai ser indefinido. Pode acontecer outra dependendo do compilador. Não vamos nos preocupar com esse detalhe, pelo menos não por agora.

A partir desse último lance eu vou armazenar lá em maiorValor o valor desse último lance: maiorValor = ultimoLance.recuperaValor();. Sem segredo nenhum.

Só que eu armazenei esse maiorValor e eu preciso de alguma forma disponibilizar ele para alguém pegar. Vamos lá! Eu vou ter um método recuperaMaiorValor, que vai ser um método const, ou seja, ele não vai alterar nada na classe: float recuperaMaiorValor() const;.

Vamos para implementação. Eu vou adicionar o avaliador e colocar a implementação, que vai simplesmente me retornar o maior valor: float Avaliador::recuperaMaiorValor() const { return maiorValor; }. Segredo nenhum, nada demais.

Aqui temos uma implementação, talvez até ingênua, de um avaliador de leilões. Como garantimos que essa implementação aqui está funcionando? O que podemos fazer para garantirmos que a nossa implementação esteja correta? Precisamos testá-la! Vamos entender como podemos testar um código no próximo vídeo.

Sobre o curso C++ e TDD: testes de unidade com Catch2

O curso C++ e TDD: testes de unidade com Catch2 possui 129 minutos de vídeos, em um total de 56 atividades. Gostou? Conheça nossos outros cursos de Linguagem C e C++ em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Linguagem C e C++ acessando integralmente esse e outros cursos, comece hoje!

Plus

  • Acesso a TODOS os cursos da plataforma

    Mais de 1200 cursos completamente atualizados, com novos lançamentos todas as semanas, em Programação, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.

  • Alura Challenges

    Desafios temáticos para você turbinar seu portfólio. Você aprende na prática, com exercícios e projetos que simulam o dia a dia profissional.

  • Alura Cases

    Webséries exclusivas com discussões avançadas sobre arquitetura de sistemas com profissionais de grandes corporações e startups.

  • Certificado

    Emitimos certificados para atestar que você finalizou nossos cursos e formações.

  • Alura Língua (incluindo curso Inglês para Devs)

    Estude a língua inglesa com um curso 100% focado em tecnologia e expanda seus horizontes profissionais.

12X
R$85
à vista R$1.020
Matricule-se

Pro

  • Acesso a TODOS os cursos da plataforma

    Mais de 1200 cursos completamente atualizados, com novos lançamentos todas as semanas, em Programação, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.

  • Alura Challenges

    Desafios temáticos para você turbinar seu portfólio. Você aprende na prática, com exercícios e projetos que simulam o dia a dia profissional.

  • Alura Cases

    Webséries exclusivas com discussões avançadas sobre arquitetura de sistemas com profissionais de grandes corporações e startups.

  • Certificado

    Emitimos certificados para atestar que você finalizou nossos cursos e formações.

  • Alura Língua (incluindo curso Inglês para Devs)

    Estude a língua inglesa com um curso 100% focado em tecnologia e expanda seus horizontes profissionais.

12X
R$120
à vista R$1.440
Matricule-se
Conheça os Planos para Empresas

Acesso completo
durante 1 ano

Estude 24h/dia
onde e quando quiser

Novos cursos
todas as semanas