Técnicas de Caixa Preta de Teste de Software Na maioria de projetos de teste, o tempo para a realização dos mesmos sempre é curto e os números de testes a serem realizados nas aplicações são inúmeros. Isso sem falar dos testes Não-Funcionais, como por exemplo, os de performance, de usabilidade, de stress, de escalabilidade, etc. É necessário se ter um planejamento bem elaborado para que todo o projeto de testes, consiga entregar um produto com no mínimo qualidade esperada pelo cliente e no prazo que foi acordado. No intuito de se realizar esse processo com certa eficiência, é necessário utilizar das técnicas de teste de software que é o tema desse artigo. Para isso, respondemos a pergunta de como fazer o processo. Entretanto, para responder a pergunta, o que e o que priorizar primeiro, temos que levar em consideração os riscos associados ao projeto de teste que devem ser levantados tão logo se inicia o projeto de todo o software. Levantados e analisados os riscos e, fazendo uso correto das técnicas de teste, é muito provável que todo o projeto de teste consiga alcançar o mínimo de qualidade esperada. O nome caixa preta vem do sentido de que nesse tipo de teste, não é necessário saber a estrutura interna de como o código foi implementado ou a tecnologia que foi utilizada. Nesse tipo de análise, essas questões são transparentes para os analistas de teste. É claro que para se ter um projeto de teste bem sucedido, é necessário usar a técnica de caixa preta e mais a de caixa branca, isto é, analisar, também, a estrutura interna da aplicação. Na nomenclatura também se utiliza o termo em inglês Black Box ou Testes Funcionais. Aqui, listo as principais técnicas de caixa preta de teste de software, com exemplos ilustrativos e, ainda, mostra como se medir a porcentagem de cobertura depois que os testes são executados para cada uma das técnicas. 1) Classe de Equivalência (Equivalent Partioning) Essa técnica é baseada na premissa que a entrada e saída de um componente podem ser particionados em classes de equivalência que, de acordo com a especificação do componente serão tratados similarmente pelo componente. Assim, o resultado de um teste usando um simples valor dessa classe de equivalência é considerado representativo em relação a uma classe. O modelo deve compreender partições de valores de entrada e saída. Cada partição deve conter um intervalo de valores, escolhidos de tal maneira, que todos os valores nessa classe têm o mesmo resultado, daí o nome equivalência. Podem ser usados valores válidos e inválidos para serem candidatos à classe de equivalência. É desejável se ter pelo menos um caso de teste para exercitar cada uma das classes de equivalência para se ter uma máxima cobertura da aplicação. Um exemplo de onde essa técnica pode ser aplicada é a classificação das notas dos alunos em A, B, C ou D de acordo com a pontuação de cada um. Se, para se conseguir uma classificação A, o aluno tem que tirar nota entre 90 e 100, B entre 80 e 89, C entre 70 e 79 e D abaixo de 70, sendo que o sistema só aceita caracteres numéricos. Graficamente, temos:
Para exercitar todas as classificações das notas que são as classes de equivalência A, B,C e D é necessário ter como entradas dos casos de teste pelo menos um representante de cada classe de equivalência, por exemplo, entradas 69 para ter como saída o valor D, o valor 75 exercitar a segunda classe de equivalência e ter a classificação C, o valor 84, para se obter B e o valor 93 para se obter A. Além desse conjunto de entradas válidas, as entradas inválidas, também seriam pertinentes para avaliar o sistema, como por exemplo, caracteres alfas-numéricos e especiais. Esses valores são mostrados na tabela abaixo: Conjunto de Casos de Teste de Classes de Equivalência Caso de Teste 1 2 3 4 5 6 Entrada 69 75 84 93 A & Saída D C B A Erro Erro Para medir a cobertura dos testes usando essa técnica, usa-se a seguinte fórmula: Cobertura Classe de Equivalência = Número de Partições Testadas * 100 % Total de Partições 2) Valores de Borda (Boundary Value Analysis) Essa técnica usa um modelo de componente que particiona as entradas e saídas dos valores de um componente em conjuntos de classificações, cujos valores de borda são exercitados. Esses conjuntos de classificações são obtidos através de especificações do comportamento do componente. Primeiramente, é necessário ter em mente quais os conjuntos de classificações compreendidas pelo sistema e, depois, levantar os valores de borda de cada um desses conjuntos. O objetivo aqui é produzir casos de teste com entradas usando os valores de borda de cada um desses conjuntos. Essa técnica é uma das mais úteis, tanto para testadores quanto para desenvolvedores, pois através dela é que são identificados muitos problemas de aceitação de valores de entradas em um sistema, principalmente, quando se usa o valor nulo ou o valor zero. Muitos desenvolvedores se valem dela para fazer testes unitários, pois eles precisam analisar se o tratamento dado por eles no código está prevendo essas condições. Como exemplo prático pode citar o exemplo anterior ou, também, para testes de datas de um sistema. No caso do exemplo anterior, alguns valores válidos e inválidos para se testar os valores de borda das classes de equivalências são mostrados na tabela abaixo: Conjunto de Casos de Teste de Valores de Borda Classe de Equivalência Entradas Saída D 69, 68,11,1,0 D C 70, 78, 79 C B 80, 88, 89 B A 90, 98, 99, 100 A Inválida A, Y, *, ^, %, # Erro Para se testar datas válidas de um sistema, a técnica de valores de borda pode ser aplicada, pois sabe que os meses com trinta dias são: Abril, Junho, Setembro e Novembro, com trinta e um dias são: Janeiro, Março, Maio, Julho, Agosto, Outubro e Dezembro e
Fevereiro com vinte e oito ou vinte e nove dias dependendo se é um ano bissexto ou não. Então podemos levantar os valores de borda para testar se um sistema está validando ou não, as entradas de data que deve ter o formato: DD/MM/AAAA. Esses valores são mostrados abaixo: Conjunto de Casos de Teste de Valores de Borda Classe de Entradas Válidas Entradas Inválidas Equivalência Janeiro 01/01/2000, 30/01/2000, 31/01/2000 32/01/2000, 31/1/2000, 31/01/00 Fevereiro 01/02/2000, 28/02/2000, 29/02/2000 29/02/2001, 30/02/2000, 31/02/2000 Março 01/01/2000, 30/01/2000, 31/01/2000 32/03/2000, 31/3/2000, 31/03/00 Abril 01/04/2000, 29/04/2000, 30/04/2000 31/04/2000, 30/4/2000, 30/04/00 Maio 01/05/2000, 30/05/2000, 31/05/2000 32/05/2000, 31/5/2000, 31/05/00 Junho 01/06/2000, 29/06/2000, 30/06/2000 31/06/2000, 30/6/2000, 30/06/00 Julho 01/07/2000, 30/07/2000, 31/07/2000 32/07/2000, 31/7/2000, 31/07/00 Agosto 01/08/2000, 30/08/2000, 31/08/2000 32/08/2000, 31/8/2000, 31/08/00 Setembro 01/09/2000, 29/09/2000, 30/09/2000 31/09/2000, 30/9/2000, 30/09/00 Outubro 01/10/2000, 30/10/2000, 31/10/2000 32/10/2000, 31/0/2000, 31/10/00 Novembro 01/11/2000, 29/11/2000, 30/11/2000 31/11/2000, 30/1/2000, 30/11/00 Dezembro 01/12/2000, 30/12/2000, 31/12/2000 32/12/2000, 31/2/2000, 31/12/00 Cobertura Valor de Borda = Número de Valores de Borda Executados * 100 % Total de Valores de Borda 3) Transição de Estados (State Transition) Essa técnica de caixa preta é baseada sobre a análise da especificação de um componente para modelar o comportamento dele em transições de estados. Ela usa um modelo de estados que o componente deve ocupar, as transições entre esses estados, os eventos que causam essas transições e as ações que resultam dessas transições. O modelo é tipicamente representado por estados, transições de estados, eventos, entradas e saídas de um componente e todos eles serem identificáveis. Os eventos causam transições entre estados e transições podem retornar para o estado original onde eles começaram. Eles são causados pelas ações que causam saídas nos mesmos. Os casos de teste devem ser elaborados para exercitar todas as transições de estados do componente. Nele deve ser especificado, o primeiro estado do componente, a entrada, as saídas esperadas, o evento que causa a transição para o próximo estado, a ação esperada causada pela transição e o próximo estado esperado. Graficamente, podemos ilustrar essa técnica da seguinte maneira:
Como exemplo de uso dessa técnica pode citar o sistema de uma companhia aérea que mostra todos os lugares de um vôo. Os lugares podem estar ocupados, reservados ou disponíveis. São estados que esse componente pode assumir. A entrada é a escolha de uma poltrona por um cliente ou uma reserva. Logo após isso, o sistema dispara um evento para atualizar aquele assento que estava disponível para ocupado ou reservado. O sistema pode fazer mais algumas validações (eventos) do tipo, se um assento ficar por mais de um tempo reservado, volta para disponível ou, ainda, se a pessoa ocupar a poltrona e não confirmar a sua escolha volta de reservado para disponível. Elaborar casos de teste para esse sistema não é uma tarefa muito fácil para os analistas, pois eles devem cobrir todos os estados que a poltrona pode assumir e as todas as condições em que elas ocorrem. Para medir a cobertura de cobertura usando essa técnica, usa-se o percentual de todos os valores válidos que foram exercitados durante o teste. 4) Gráfico de Causa e Efeito (Cause & Effect Graphing) Gráfico de causa e efeito usa um modelo de relações lógicas entre causas e efeitos para um componente. Cada causa é expressa como uma condição, que pode ser verdadeira ou falsa (condição Booleana) de entrada ou uma combinação de entradas para o componente. Cada efeito é expresso como sendo uma expressão Booleana representando uma saída, ou uma combinação de saídas para o componente ocorrido. O modelo é tipicamente representado como gráfico Booleano relacionando as entradas e saídas Booleanas, usando os operadores Booleanos: AND, OR, NAND, NOR e NOT. Desse gráfico, é produzida uma tabela de decisão representando as relações lógicas entre causas e efeitos. Os casos de teste devem ser projetados para exercitar as regras, que definem a relação entre as entradas e saídas dos componentes, onde cada regra corresponde a uma possibilidade única de entrada para o componente que tem sido expresso como booleano. O caso de teste deve identificar o estado booleano de cada causa e o estado para cada efeito.
Como exemplo dessa técnica pode citar os casos de teste para validar as operações bancárias feitas por um correntista. As entradas (causas) para esse projeto de teste seria o tipo de conta, limite máximo, saldo atual e montante de débito. Os efeitos são flag para saber se a operação foi realizada ou não e o saldo atual da correntista. Os casos de teste que podem ser levantados para validar essa aplicação são inúmeros e, o resultado deles, depende das operações booleanas para saber se será possível ou não realizar a transação. Cobertura Causa-Efeito = Número de Regras Exercitadas * 100 % Total de Regras 5) Técnica de Sintaxe (Syntax Testing) Essa técnica de caixa preta é baseada sobre a análise da especificação de requisitos. Nesse documento são especificados os valores possíveis que uma entrada do sistema pode receber. No entanto, nesse documento, as entradas inesperadas, na maioria das vezes, não são tratadas. É nesse contexto que se insere a técnica de Técnica de Sintaxe. Ela consiste em ter como entradas esperadas e as não esperadas para validar a especificação de requisitos e avaliar o comportamento de um sistema quando esses valores não especificados são usados. Ë nessa técnica onde são encontrados os principais problemas de tratamentos de exceções, já que ela se divide em Sintaxe Válida e Sintaxe Inválida. Na primeira, somente os valores esperados são utilizados, como por exemplo, valores numéricos em campos que remetem a uma quantidade de dias de férias que um funcionário tem a receber, saldo de uma conta, livros em um andar de uma biblioteca, valores alfa em campos de nomes, sobrenomes, endereço, etc. Já na segunda técnica, a Sintaxe Inválida, o objetivo é exercitar as entradas inválidas, como inserção de valores alfa em campos numéricos, inserção de valores numéricos em campos de valores alfa, inserção de caracteres especiais, inserção de valores float em campos numéricos e inteiros, inserção de valores negativos em campos de valores numéricos e inteiros, inserção de vírgulas e pontos em campos de valores numéricos. Todas essas entradas visam validar qual será o tratamento dado pela aplicação quando esses valores forem usados. Ë desejável que mensagens de erro e de ajuda sejam colocadas na tela no sentido de guiar o usuário a usar os valores corretos. Outro aspecto que vale mencionar é que na maioria das especificações de requisitos, nem os valores aceitáveis são especificados. Nesse caso, seria interessante descobrir quais os tipos de valores que os campos podem receber dando uma olhada no banco de dados dessa aplicação. O arquiteto de teste aliado a um desenvolvedor que tenha mais proximidade consegue levantar essa informação. Nesse caso, o levantamento dessas informações é essencial para a aplicação dessa técnica. Cobertura Teste Sintaxe = Número de Testes Sintaxe Exec * 100 % Total de Casos de Teste!
6) Teste Randômico (Random Testing) Essa técnica consiste em utilizar um algoritmo pseudo-randômico para se escolher quais os casos de testes serão usados para validar uma funcionalidade de uma determinada aplicação dentro um conjunto definido de valores. O objetivo aqui não é fazer testes Ad- Hoc (testes sem resultados previamente esperados e sem um propósito) e, sim, simplesmente, em uma escolha dos casos de teste que foram previamente elaborados. Normalmente, se aplica essa técnica quando o tempo de teste foi reduzido em virtude de eventualidades que aconteceram no projeto. Um dos exemplos clássicos para exemplificar essa técnica é o Diagrama de Pareto. O princípio se baseia em uma análise quantitativa, ou seja, 80% de todos os problemas são oriundos de 20% das causas potenciais. No contexto de teste de software, se baseia em escolher 20% de todos os casos de testes criados, cobrindo 80% das funcionalidades de todo o projeto. Para ter esse conjunto de casos de teste, é necessário ter uma relação de requisitos do sistema x testes criados. Depois de feita essa relação, escolher os casos de testes que mais aparecem nessa relação. Um exemplo que ilustra essa técnica é mostrado abaixo. Segundo a tabela abaixo, temos um total de 30 casos de testes criados hipoteticamente ligados aos requisitos básicos de um sistema de uma biblioteca. Seguindo o principio de Pareto, se testarmos, os casos de teste, 2,3,7,18,22 e 25 que são os 20% principais e que mais cobrem funcionalidades, estaremos cobrindo 80% das funcionalidades totais da aplicação. Sistema de uma Biblioteca Funcionalidades Básicas Casos de Testes criados Inclusão de Dados de Usuário 1,2,3,4,5,14 Edição de Dados de Usuário 2,5,6,7,8,9,13 Deleção de Dados de Usuário 2,3,7,10,11,12 Reserva de Livros 15,16,17,18,19,20,21,22 Adição de Livros 18,22,23,24,25,26 Remoção de Livros 25, 27,28,29,30 Cobertura Teste Randômico = Número de Testes Exec Randomic * 100 % Total de Casos de Teste "