Você está na página 1de 14

Teste de Software

UNIDADE 2
Para início de conversa

Olá, caro(a) aluno(a)! Tudo bem?

Iremos começar nossa segunda unidade da disciplina Teste de Software. Na unidade passada discutimos
sobre conceitos de defeitos de software, quais possíveis impactos que esses defeitos podem causar em
um produto de software. Mostrei também a você os conceitos de validação e verificação de software.

Então, é sempre bom estar atento que VERIFICAÇÃO significa atestar se o produto que foi desenvolvido
está adequado com a especificação elaborada, já a VALIDAÇÃO é se o produto que foi desenvolvido está
de acordo com a expectativa do cliente. Também apresentei para você algumas estratégias de testes.
Agora, nesta unidade 2, vamos aprofundar mais um pouco a discussão sobre elaboração de casos de
testes.

Guarde essa ideia!

Apresento para você a seguinte ideia:

Figura 1
Fonte: Pressman, R.; Engenharia de Software: uma abordagem profissional 7 ed. 2011.

A ELABORAÇÃO DE CASOS DE TESTES

A hierarquia dos testes que são executados à medida que o software vai sendo construído.

Inicialmente, os desenvolvedores estão preocupados em atestar que o código está funcionando plena-
mente, realizando os testes de unidade. À medida que os componentes são desenvolvidos e entregues,
são realizados os testes de integração entre os componentes; posteriormente é realizado um teste de

1
integração entre os módulos, o sistema é posto em validação pelos usuários e analistas de negócio e,
finalmente, o produto é homologado. Esse foi um simples resumo do que eu mostrei para você na unidade
passada.

Na prática, o time de desenvolvimento deve ser responsabilizado por iniciar os testes de um produto de
software, por isso é fundamental ter cuidado na utilização da equipe de desenvolvimento no processo de
testes.

Alguns dizem: quem é mais capacitado para testar um produto do que a própria pessoa que desenvolveu?

Isso é um questionamento perigoso porque, psicologicamente, um desenvolvedor tem o interesse de de-


monstrar que o produto que ele desenvolveu é isento de defeitos, foi construído no prazo estipulado e que
respeita os requisitos especificados. Por isso, que se recomenda que o time de desenvolvimento realize
testes apenas em nível de código, bem como os demais testes sejam entregues a membros externos à
codificação do produto.

O recomendado é que os testadores sejam membros do projeto especializado apenas para testes de re-
quisitos e do produto, isso quer dizer que se a empresa que você trabalha tem condições de realizar esse
tipo de abordagem, isto é um ótimo sinal. Porém, nem todas as empresas têm condições de trabalhar nes-
se nível de especialização de desenvolvimento, quando a quantidade da equipe é reduzida, normalmente
o analista de requisitos acumula a função de analistas de testes.

TESTES DE DESENVOLVIMENTO

Uma vez que você entendeu a relação entre as atribuições de um testador e os papéis dos membros do
projeto, vamos começar a discutir sobre como realizar os testes mais básicos no sistema.

A abordagem mais simples de escrever testes, são os famosos testes de unidade. Se você programar de
forma estruturada a menor unidade que temos em um sistema são os ARQUIVOS, já no caso de orientação
a objetos a menor unidade é uma CLASSE. Testes unitários servem para garantir que todas as funções ou
métodos de negócio possuem o funcionamento correto de forma isolada. Bem, eu destaquei essas duas
expressões “de negócio” e “de forma isolada” por um bom motivo.

Entenda o motivo de tais destaques!

Primeiramente, os testes unitários deverão garantir o funcionamento da sua regra de negócio, isso exclui
testes de interface gráfica. Idealmente se você codificar um projeto de forma arquiteturalmente correta, a
cobertura dos testes unitários atenderá 100% de sua regra de negócio.

Bom, acho que você ainda não entendeu onde eu quero chegar com esses “se”.

Vou ser mais claro! O que é um projeto arquiteturalmente correto?

A resposta é simples: é um projeto sem violações de camadas.

2
Bom, se você não sabe o que significa um projeto sem violações de camadas é necessário relembrar sobre
arquitetura de software.

As violações de camadas ocorrem quando um desenvolvedor escreve um código em um lugar inapropriado


para aquele código.

Exemplo

Exemplificando...

Imagine que você está na camada de negócio e faz um acesso direto ao banco de dados, isso é uma vio-
lação de camada, pois quem realiza acesso aos dados são as classes que estão na camada de acesso aos
dados (DAO do inglês Data Access Object).

Outro exemplo clássico de onde eu quero chegar é a existência de códigos de regra de negócio na cama
de apresentação. Não se deve tratar os dados de negócio em classes que fazem controle a camada de
apresentação, pois essas classes devem apenas manipular os componentes visuais da tela.

É uma prática comum de desenvolvedores aplicarem filtros ou transformarem dados que são entregues
pela camada de negócio dentro da camada de apresentação. Então, você deve estar ligando os pontos
neste momento, os testes unitários devem realizar a cobertura do meu código de negócio e não da cama-
da de apresentação, isso significa que se eu cometer uma violação de camada, e parte da minha regra
de negócio estiver espelhada entre a camada de apresentação e a camada de negócio, apenas parte da
minha regra de negócio estará sendo coberta pelos testes unitários.

Guarde essa ideia!

Isto significa que se os testes unitários não conseguem garantir a cobertura total das
regras de negócio do sistema, não foram desenvolvidos corretamente e não agrega
muito valor a confiabilidade do produto. Ou seja, para que os testes unitários agreguem
valor à confiabilidade do produto é necessário que a codificação do produto esteja
arquiteturalmente adequada.

O outro ponto que eu destaquei anteriormente é que os testes unitários atestam o funcionamento do
produto de forma isolada. Para você ter uma melhor compressão eu irei mostrar, através de um exemplo,
como projetar um teste unitário.

3
Praticando

Primeiramente, apresentarei uma classe chamada CalcularPagamentoServiceTest, a uma classe de exem-


plo que possui um método chamado de calcularDescontosInss.

Figura 2
Fonte: Criação do Professor

Esse método recebe como parâmetro uma variável decimal contendo um valor de salário. Se o salário for
menor igual a 1500,00 retorna o valor de desconto de 9%, se for maior, retorna um desconto no valor de
11%. Para realizar testes utilizei o framework JUNIT de testes unitários. Criei uma classe chamada Calcu-
larPagamentoServiceTest que tem a finalidade de testar os métodos da classe CalcularPagamentoService.

Figura 3
Fonte: Criação do Professor

4
Cada método dessa classe de testes é responsável por testar um cenário diferente do método de calcular
o desconto de INSS. Perceba que o método de cálculo de descontos tem uma estrutura condicional, isso
significa que esse método permite apenas duas situações, por isso que foram criados dois métodos de
testes, cada um deles testam uma situação diferente.

No primeiro método eu criei um funcionário João, com matrícula 1 e salário de 1.000,00 reais. Fiz o cálculo
do desconto de INSS e comparei o valor retornado com o valor esperado de 90,00. No segundo caso eu
criei uma funcionária Maria, com matrícula 2 e o salário de 2.000,00 reais. Realizei o cálculo de desconto
e comprei com o valor esperado do 220,00 reais.

Com esses dois casos de testes garanti a cobertura de 100% da classe de negócio, se você perceber a
execução dessa classe testes, a execução passa por todas as linhas da classe de negócio, por isso que eu
afirmei que garanti a cobertura de 100% da minha classe de negócio.

Testes unitários são muito caros porque requer um esforço muito grande de desenvolvimento. Mostrei a
você um exemplo simples de testes de um método com apenas duas situações.

Agora imagine: uma classe repleta de métodos com estruturas de repetição e condicionais aninhadas.
A quantidade de casos de testes unitários seria imensa, um esforço muito grande de desenvolvimento.
Por este motivo testes unitários são bastante caros para o desenvolvimento, porém necessários; com
eles é possível criar e testar diversos casos de testes de forma rápida, atestando a qualidade do código
produzido.

A grande vantagem de se ter testes unitários de todos os métodos é que garante a regressão de testes
quando há alterações no código. Toda vez que seu código sofrer alteração poderá gerar impactos nos
testes unitários, que servem para garantir que os métodos continuem retornando resultados esperados
pelos testes, caso alguma alteração afete o comportamento do método, os testes unitários acusarão que
o novo código não está agindo como esperado.

Guarde essa ideia!

É bem importante realizar testes unitários do seu sistema porque além todas as van-
tagens já citadas anteriormente, os testes unitários auxiliam na documentação do pro-
jeto. Por incrível que pareça observar os testes unitários ajudam bastante os desen-
volvedores que irão realizar manutenção no sistema, os testes unitários dão uma boa
visão das entradas que os métodos esperam e quais são as saídas que esses métodos
deveriam retornar.

Agora que você já entendeu, caro(a) estudante, mais sobre testes unitários chegou a hora de testar um
pouco se você conseguiu visualizar bem a questão arquitetural da coisa.

5
Observe a imagem a seguir:

Figura 4
Fonte: Criação do Professor

Neste caso, o sistema em desenvolvimento possui essas três classes:

• A GerenciadorDFuncionarioService que possui dois métodos: calcularVencimentos e incluir-


Fuincionario;
• A classe GerenciadorDeFuncionarioService no método de calcularVencimentos invoca o servi-
ço de calcularProdutividade da classe ProdutividadeService;
• Já o método de incluirFuncionario invoca o método inserir da classe FuncionarioDAO.

Se você percebeu nós temos, a título de negócio, a troca de mensagens entre GerenciadorDeFuncionario-
Service e ProdutividadeService e realizando passagem de dados entre a camada de negócio e persistên-
cia, a chamada ao método de inserir. No exemplo passado, eu mostrei um teste simples de uma classe
isolada com apenas um método. Agora estamos vendo algo mais real, que são classes com associações
com outras classes.

Neste caso, como você escreveria um teste unitário?

Então, como você sabe, na própria informática, na maioria dos casos, não existe certo nem errado, porém
existem formas mais apropriadas de resolver problemas. Se você prestou bem atenção em como estou
conduzindo essa explicação, deixei uma lacuna de propósito para que você estivesse na curiosidade de
se perguntar, porque eu não expliquei claramente o motivo de destacar que testes unitários servem para
garantir que todas as funções ou métodos de negócio possuem o funcionamento correto de forma isolada.
Antes de explicar sobre como escrever testes unitários, deixei destacado essas duas características da
geração de casos de testes unitários, porém, apenas expliquei a primeira. Então você deve estar se per-
guntando:

Por que os testes unitários devem ser escritos de forma isolada?

6
Qual a necessidade disso?

E como farei isso?

Primeiramente, o nome testes unitários já reflete a necessidade de testar unitariamente o código, se você
está falando de um sistema orientado a objetos a menor unidade é uma CLASSE.

Todas as classes de negócio precisam receber a cobertura de testes unitários para garantir o seu funcio-
namento, porém, essa classe precisa ser testada de forma isolada para que seu funcionamento não haja
dependência com outras classes ou serviços.

Exemplo

Vou te dar um exemplo bem simples, imagina que você está escrevendo um método que gera uma etique-
ta de postagem para endereço no seu comércio eletrônico. Nesse seu método há uma integração com o
webservice dos correios que ao passar um CEP ele retorna um endereço válido. Ao escrever o teste unitá-
rio do seu método, você vai encontrar a dependência para um serviço externo, que se teu computador não
estiver com conexão à internet você não poderá realizar a execução do teste unitário; se tiver algum erro
no serviço dos correios pode comprometer teu teste unitário. O mesmo vale para classes do teu sistema,
se o método que é invocado estiver com defeito, então teu teste unitário também estará com defeito, e
na realidade o que está acontecendo é que teu método está funcionando corretamente, porém, os dados
oriundos de outros serviços estão comprometendo a execução dos teus testes.

Então na prática o teu método está certo, o teste unitário deve garantir que se o teu método tiver as en-
tradas corretas, o funcionamento dele vai estar correto.

Como fazer para se livrar das dependências normais que a maioria dos métodos tem com outras classes?

Querido(a) aluno(a), no desenvolvimento existe um tipo de objeto chamado de “Mocks”, os quais simulam
uma saída para algum serviço que você utilize, você consegue programar saídas esperadas de integrações
sem necessitar da real integração com aquele serviço na hora de realizar os testes unitários. Então esses
objetos são utilizados para receber resultados esperados, bem como garantir que se o teu método receber
resultados corretos, irá funcionar perfeitamente.

Leituras complementares

Você pode entender mais sobre Mocks acessando esse link.

Então para ficar mais claro, como proceder naquela situação do cálculo de vencimentos e persistência do
funcionário, colocada mais em cima?

7
Ao realizar o teste unitário do cálculo de vencimentos, o qual depende o serviço de cálculo de produtivi-
dade, na hora de você escrever o teste unitário do método do cálculo de salários, você deve utilizar um
objeto mock para simular o retorno de um cálculo de produtividade correto. Assim, você não irá depender
do funcionamento correto do cálculo de produtividade para que o método de cálculo de vencimentos seja
testado unitariamente.

E o caso da persistência?

Você pode adotar a mesma estratégia de utilizar um mock para simular a gravação no banco de dados.
Existem mocks sofisticados que simulam realmente o acesso ao banco de dados, se você tiver curiosidade
de entender sobre esses objetos, acesse o link.

Então em resumo, os testes de desenvolvimento iniciam na geração de casos de testes unitários, para
garantir o pleno funcionamento individual de cada unidade do teu sistema, após você escrever testes uni-
tários e realizar uma bateria de testes isolados, é hora de realizar integrações entre os módulos e realizar
testes de integração para comprovar a efetividade do teu sistema.

Geração de casos de testes de integração

Agora que você aprendeu como realizar testes unitários em um sistema, vamos começar a discutir como
realizar testes de integração entre os módulos desenvolvidos.

Mas agora você deve estar se perguntando, se eu já escrevi testes unitários para cada classe do meu
sistema não é garantia que ele estaria funcionando por completo?

Na teoria parece bem clara essa resposta, se cada pequeno pedaço do software foi testado e a cobertura
chega a 100%, significa que todo meu código está bem coberto pelos testes unitários, então ele está
100% ok; mas, na informática nem tudo é exato. Infelizmente, algumas interfaces pode haver problemas
de entendimento de dados ou existir o fator humano no meio que pode colocar entradas de dados não
cogitadas.

Infelizmente, o desenvolvedor não consegue prever tudo que o usuário poderá fazer e todas interface de
troca de dados precisam ser testadas.

Exemplo

Vou te dar um exemplo bem superficial, um exemplo que na prática não aconteceria,
porém, vai te fazer entender melhor o tipo de falha de troca de dados entre as interfaces.

Vamos imaginar que você desenvolveu um método de calcular salário de um funcionário, e outra equipe
desenvolveu um método para retornar o valor de um benefício que a empresa dá, em decorrência do salá-
rio do funcionário. Você precisa interagir com esse método para somar esse benefício no cálculo salarial
do seu empregado; porém a equipe que desenvolveu o método precisa de um valor para realizar o cálculo
do benefício.
8
Pois bem, há uma interface entre os dois métodos que exige uma passagem de parâmetro, neste caso,
o valor salarial. Então, imagine que você retire o desconto de INSS do salário do funcionário e passe o
valor descontado para o método do cálculo do benefício, por outro lado o método do cálculo do benefício
assuma que você não realizou o desconto e faça essa subtração também. Os dois métodos isoladamente
estarão corretos, porém, quando você une os dois o resultado fica inconsistente. São essas anomalias de
troca de dados pelas interfaces que os testes de integração visam encontrar.

É importante saber que existe duas formas de realizar testes de integração em um sistema, uma chamada
de “big bang”, a qual os testes são realizados apenas mediante a entrega completa do sistema e a outra
incremental, na qual o sistema passa sobre testes constantemente a cada pedaço de incremento.

Praticando

Testar um sistema nunca é fácil, ambas formas vão te dar muito trabalho, se você optar
por testar cada pequeno incremento, você tem que testar as novas funcionalidades e
garantir a regressão. Entrarei em mais detalhes na próxima unidade. Se você resolver
testar o sistema como todo, possivelmente o caos estará instalado, pois a grande quan-
tidade de funcionalidade sendo testada de uma vez, vai gerar um volume muito grande
de defeitos e de retrabalho para corrigir as integrações com os módulos. À medida me-
nos traumática de testar software que eu recomento, é fazer testes a cada incremento,
pois correções de defeitos e melhorias são desenvolvidas ao longo da construção do
software.

Agora vamos ser mais práticos, eu tenho um módulo do sistema desenvolvido como faço para realizar os
testes?

Primeiramente, os testes precisam ser planejados antes do desenvolvimento ser iniciado, o planejamento
de testes é feito concomitantemente com a especificação das regras de negócio do sistema. Os casos de
testes vão sendo evoluídos à medida que tua especificação vai sendo construída.

Mas como vou fazer isso?

Normalmente, as especificações dos requisitos são feitas utilizando os casos de uso. Com base nos teus
casos de uso você começa a criar uma planilha com casos de testes.

Observe a imagem a seguir:

9
Figura 5
Fonte: http://sereduc.com/psB1vn

O caso de uso é composto de três fluxos:

• Principal
• Alternativo
• De exceção

O objetivo de gerar casos de testes, a partir de caso de uso, é de fornecer entradas e saídas que contem-
ple as combinações possíveis de todos fluxos.

Exemplos

Vamos a um caso bem simples, o cadastro de um funcionário:

Quadro 1
Fonte: Criação do Professor

10
Imagine um caso de uso bem simples de uma tela de cadastro. Você sabe que casos de uso possuem
muitas regras, diversos fluxos alternativos e excepcionais. Na derivação de um caso de uso para gerar
um caso de teste, você deve mapear todas as entradas de dados para que os fluxos sejam executados e
comparar as saídas para ver se o sistema está se comportando como o esperado.

Então é necessário criar um script de testes a partir dos casos de uso. Inicialmente, você precisa mapear
quais cenários e qual fluxo ele precisa passar para ser completado.

Veja o quadro a seguir:

Quadro 2
Fonte: Criação do Professor

Primeiramente, você precisa estabelecer por quais fluxos cada cenário desse irá passar, o ideal é que
você consiga realizar todas combinações possíveis de cenários para realização dos testes. Após isso, você
precisa documentar quais serão as entradas de dados em cada passo e quais as saídas esperadas para
cada cenário.

Neste exemplo para você alterar um funcionário, você deverá passar primeiro pelo fluxo básico de consul-
ta de funcionário primeiro. Para você testar se o sistema está validando as informações corretas e testar
erros, você precisa mapear os fluxos que o testador deverá passar até encontrar o fluxo de exceção. Para
gerar um caso de testes vou te mostrar outro exemplo, imagine que você precisa testar um sistema ban-
cário para realizar um saque em um caixa eletrônico.

Querido(a) aluno(a), preste atenção no seguinte casos de uso:

11
Quadro 3
Fonte: Criação do Professor

Para realizar a geração de casos de testes baseada em casos de uso, você pode realizar a quantidade de
cenários gerando uma tabela de quantas combinações de fluxos existem utilizando a fórmula do exemplo
anterior.

12
Vamos realizar a montagem de um cenário utilizando as seguintes ações:

Quadro 4
Fonte: Criação do Professor

Para cada cenário é necessário analisar o caso de uso que foi elaborado, é necessário realizar todas as
combinações possíveis para garantir a cobertura dos testes. É um trabalho realmente árduo, porém os
resultados são muito expressivos. Todos os sistemas precisam ser bem testados para não gerar grandes
quantidades de defeitos para o usuário final.

Chegamos ao fim da unidade 2. Apresentei a você como fazer testes unitários e como gerar casos de
testes de integração, na próxima unidade iremos continuar discutindo sobre casos de testes, como você
pode automatizar os testes.

Na última unidade, vamos discutir sobre métricas de testes e testes de requisitos não funcionais.

Acesse o Ambiente Virtual

Lembre-se: é muito importante que você realize todas as atividades disponíveis no


Ambiente Virtual de Aprendizagem (AVA), pois possuem caráter avaliativo.

Espero você na nossa próxima unidade. Bons estudos!

Referências Bibliográficas

Sommerville, I; Engenharia de software 9 ed. 2011; 528p

Pfleeger, S. L.; Engenharia de software: teoria e prática 2004.

Pressman, R.; Engenharia de Software: uma abordagem profissional 7 ed. 2011.

13

Você também pode gostar