quinta-feira, 29 de novembro de 2012

Specification By Example


Terminologia e Definição



O termo Specification by Example (Especificação por Exemplos) surgiu segundo seu criador Gojko Adzik (2011) com o intuíto de unificar em um só termo as seguintes técnicas, metodologias e práticas ágeis:



  • Agile acceptance testing
  • Test-Driven Development
  • Acceptance Test-Driven Development
  • Example-Driven Development
  • Story testing
  • Behavior-Driven Development



Segundo Gojko o fato de as mesmas práticas possuírem tantos nomes diferentes reflete o grande número de inovações nesse campo no cenário atual da industria de software. Sobre essas diferentes nomenclaturas Gojko afirma:



Se queremos os usuários finais (stakeholders) mais envolvidos, o que é o principal objetivo dessas práticas, precisamos usar os nomes corretos para as coisas corretas e parar de confundir as pessoas.



O autor decidiu por tanto simplificar essa terminologia na comunidade de software para que as pessoas possam usufruir de seus benefícios sem entrarem em discuções improdutivas sobre terminologias. Specification by Example é o resultado de um conjunto de estudos de caso realizados por Gojko com mais de 50 projetos de desenvolvimento de software do mundo real. Projetos que variam em tecnologia (de websites a softwares para desktop) e em metodologias (Extreme Programing, Scrum, Kanban e outras metodologias normalmente agrupadas sobre os termos agile e lean). Specification by Example pode ser definido como um suplemento de práticas que podem ser incorporadas a qualquer metodologia de desenvolvimento atual.





Necessidade


Construir o produto certo e construir certo o produto são dois objetivos distindos. Porém os dois são necessários para se obter sucesso. Segundo Gojko tradicionalmente, construir o produto certo requeria enormes especificações funcionais, documentação e longos ciclos de teste, atualmente em um mundo de releases semanais isso não funciona. Ainda segundo Gjko é necessário uma solução para:

  • Evitar super-especificações inúteis; Evitar gastar tempo em detalhes que mudarão antes do primeiro fragmento de trabalho ser desenvolvido.
  • Ter documentos legíveis que explicam oque o sistema faz para que se posso modificá-lo facilmente.
  • Checar de maneira eficiente que o sistema faz o que as especificações dizem.
  • Manter a documentação relevante e confiável com o menor custo de manutenção.
  • Colocar tudo isso em processos baseados em iterações, de forma que a informação do trabalho a ser feito é produzida sob demanda.

Specification by Example unificou padrões e processos que times do mundo real utilizaram para atingir esses objetivos.


Padrões de processo

Os padrões estabelecidos por Specification by Example são assim denominados no sentido em que eles são elementos recorrentes usados por diferentes times; São resultados de uma série de experimentos empíricos com projetos e times reais e não de um modelo teórico de elaboração de processos.




Figura 1. Principais padrões de processo de Specification by Example.

1. Deriving scope from goals (Derivando o escopo dos objetivos): Nessa etapa a equipe que criará o software deve derivar os escopos de desenvolvimento a partir dos objetivos estabelecidos pelos stakeholders. Segundo Gojko é um erro esperar que os usuários envolvidos digam qual o escopo de desenvolvimento pois os mesmos não são designers de software. O mais apropriado nessa etapa é elencar os objetivos que os stakeholders querem atingir com o software em questão. Em outras palavras, qual ou quais os problemas que a “solução” deve ser capaz de resolver. Começa-se com um objetivo de negócios o time de desenvolvimento colabora definindo o escopo que vai atingir este objetivo.

2. Specifying collaboratively (Especificando colaborativamente): O time nesse etapa deverá juntamente com os stakeholders especificar colaborativamente o comportamento esperado para cada funcionalidade do escopo. Desenvolvedores, testadores e analista de negócios devem participar nessa etapa pois cada um tem um contexto único e habilidades que podem ajudar a resolver questões que de outra forma poderiam passar desapercebidas gerando re-trabalho no futuro.

3. Illustrating using examples (Ilustrando utilizando exemplos): Ao invés de esperar que as especificações sejam expressadas precisamente em uma linguagem de programação durante a implementação, deve-se ilustrar o comportamento  esperado do sistema utilizando exemplos antes do desenvolvimento ser iniciado. O time de desenvolvimento trabalha com os stakeholders para identificar exemplos-chave que descrevem a funcionalidade esperada. Desenvolvedores e testadores dão exemplos adicionais que revelam áreas antes ignoradas da funcionalidade evitando assim gaps funcionais e faz com que todos tenham um conhecimento distribuído sobre aquilo que precisam entregar.

4. Refining the specification (Refinando a especificação): Nessa etapa o time de desenvolvimento deve remover informações irrelevantes dos exemplos tais como detalhes que tornam os exemplos extensos e verbosos sem adicionar informações úteis. Segundo Gjko deve-se identificar o que o software deve fazer e não detalhar o como fazer. Os resultados dessa etapa podem ser considerados critérios de aceitação, que ao mesmo tempo são especificações para o desenvolvimento e futuramente quando automatizados farão parte dos testes de regressão.

5. Automating validation without changing specifications (Automatizando as validações sem modificar as especificações): Com os exemplos especificados e refinados o time pode usá-los como meio de validar o produto. O sistema será validado diversas vezes durante o desenvolvimento para assegurar que o mesmo atinge os objetivos. Rodar essas validações manualmente introduziria um delay desnecessário e o feedback seria lento. A solução para isto é automatizar as validações, porém essa automação deve também ser acessível aos stakeholders e não deve modificar as especificações originais. O time de desenvolvimento literalmente deve escrever a automação com as mesmas palavras utilizadas para criarem os exemplos, pois scripts e código fonte não têm a capacidadade de contar expressar adequadamente o contexto de negócio em que estão inseridos e documentação puramente textual fica desatualizada rapidamente. Portanto nessa etapa a validação automática do sistema, suas especificações por exemplos e seus critérios de aceitação tornam-se um só. O resultado dessa etapa gera especificações executáveis.  

6. Validating Frequently (Validando frequentemente): Gjko afirma que a documentação tradicional de software fica desatualizada antes mesmo de o sitema ser lançado. A validação frequente é um produto das especificações executáveis ela sincroniza o que foi especificado com o que o sistema realmente está fazendo.

7. Evolving a documentation system (Evolução de um sistema de documentação): Nessa etapa o time de desenvolvimento mantém a documentação viva (especificações executáveis - testes) atualizada com as modificações requisitadas pelos stakeholders para incrementar o software. As especificações e testes estando em um mesmo lugar facilitam essa mudança pois concentram os esforços em apenas uma fonte de informações. Desenvolvedores utilizam essa documentação como especificações, testadores utilizam como testes e os analistas de negócio utilizam para avaliar o impácto que uma mudança ou especificação futura pode ter no sistema.
Os exemplos são tratados como fonte única de verdade, sendo este um aspecto chave desta metodologia. Desta forma, evita que diferentes colaboradores de uma equipe de desenvolvimento gerem e trabalhem somente em seus próprios documentos. Neste contexto, pode-se afirmar que a eficácia da entrega do software é significativamente afetada pela constante necessidade de coordenação e sincronização destas diferentes “versões da verdade”. Fazendo uso de SbE todos os membro da equipe participam da criação da única fonte de verdade, sendo assim nela encontra-se a compreensão de todos. Estes exemplos são utilizados para proporcionar precisão e clareza, para fazer uso desta informação tanto como uma especificação como um teste funcional. Qualquer informação considerada relevante durante o ciclo de desenvolvimento é adicionada a esta fonte de exemplos única, sem requerer qualquer necessidade de tradução ou interpretação.

Documentação Viva

Segundo Gojko a questão da documentação gerada pelo Specification by Example advém parte do ATTD e parte do BDD, ambas com focos diferentes que quando unidas proporcionam uma totalidade na cobertura e profundidades acerca das especificações do sistema. O ATTD detém seu foco na automação dos testes funcionais, de regressão e são alvos mais claros para o desenvolvimento. O ATDD é mais útil para a adoção inicial quando se tem muitos problemas relacionados a qualidade funcional do sistema. Já o BDD centra-se no processo de especificação de cenários por meio do comportamento do sistema, podendo servir como base para fundamentar as atividades de entrega de software, a médio e curto prazo. O BDD centra-se na construção do entendimento comum entre as partes interessadas através da colaboração e esclarecimento de especificações. É necessário saber o que um sistema faz para ser capaz de analisar os impactos das mudanças sugeridas, apoiá-lo, e solucionar problemas. Muitas vezes, a única maneira de descobrir o que o sistema faz é olhar para o código-fonte da linguagem de programação e aplicar engenharia reversa na funcionalidade do negócio. Logo uma boa documentação é útil para além do desenvolvimento de software. 
A solução ideal seria um sistema de documentação que é fácil e barato de manter, de forma que ele possa ser mantido de acordo com a funcionalidade do sistema, mesmo se o código da linguagem de programação subjacente é alterado com frequência. O problema com qualquer tipo de documentação completa é, de fato, o custo de manutenção. Quando um sistema de software é continuamente validado contra um conjunto de especificações executáveis, uma equipe pode estar certa de que o sistema irá fazer o que as especificações dizem, ou, em outras palavras, que as especificações vão continuar a descrever o que o sistema faz. Essas especificações vivem com o sistema, e elas são sempre consistentes, e pelo fato da noção das diferenças entre as especificações e a funcionalidade de fato implementada de forma imediata, esta documentação pode ser mantida consistente a um baixo custo. Estas especificações executáveis ​​criam um corpo robusto de documentação, uma fonte autorizada e não ambígua de informações sobre a funcionalidade de um sistema,fazendo uma analogia, cada uma das especificações seriam como páginas do livro que é o próprio sistema de documentação viva. 
Gojko ainda afirma que a documentação viva substitui todos os artefatos que as equipes precisam para entregar o produto certo, ela pode até mesmo apoiar a produção de manuais de usuários (embora provavelmente não substituí-los). As especificações são definidas conforme e concomitante a evolução do sistema, a documentação resultante é incremental e assim torna-se barata de escrever. Ao mesmo tempo em que os processos de negócio são derivados é possível executá-los exercitando e auxiliando o processo de desenvolvimento do software, já que se trata de algo mais leve e intrinsecamente mais ligado ao código real, é uma documentação real. Aplicar Specification by Example somente pelo fato do ganho por meio dos testes de regressão acaba por não justificar o investimento feito em longo prazo para implementá-lo já que conforme as estimativas dos custos de software, aponta que a eficiência na remoção dos defeitos por meio de testes de regressão é de 23% em média. Esta prática permite a criação de documentação viva sem necessidade de anos de prática pela sua forma mais natural de escrita, criando documentação por meio de especificações executáveis.
Figura 2 Tipo de documentação que deve ser escrita
A documentação viva é tão importante quanto o sistema que se está entregando, a ideia de que a documentação é um produto chave é o cerne deste método. Algumas vantagens em ter uma documentação viva são apontadas por Gojko: 
  • Ajuda as equipes a longo prazo  evitando os problemas mais comuns com manutenção;
  •  Ajuda as equipes a criarem documentação útil que irá facilitar a evolução do software ao longo do tempo;
  •  Ajuda a evitar problemas de manutenção causados ​​pela falta de conhecimento compartilhado.

sábado, 17 de novembro de 2012

Como o Cucumber Funciona



O cucumber é uma ferramenta de linha de comando. Seu módo de funcionamento e suas camadas operam basicamente da seguinte forma:

Camada de negócios

  1. O Cucumber lê as especificações escritas em arquivos de texto plano chamados features;
  2. Examina esse tipo de arquivos a procura de scenarios;
  3. Cada scenario é uma lista de steps que o Cucumber irá tentar executar;
  4. Cada step é escrito em linguagem natural de domínio da aplicação. Para que sejam facilmente lidos e interpretados por pessoas sem a necessidade de qualquer conhecimento técnico;

Camada de tecnologia

  1. Para realizar quaisquer ações os steps# precisam estar implementados em um arquivo separado chamado de  step definition;
  2. A implementação dos steps dentro dos steps definitions é realizada através do support code ou código de suporte, que pode ser escrito em uma variada gama de linguagens¹;
  3. O support code é o responsável por se encarregar das tarefas de automação, normalmente isso envolve uma automation library ou biblioteca de automação que então entrará em contato direto com o sistema alvo do teste.

Essa hierarquia de camadas pode ser melhor observada na figura 1.0.

Figura 1.0. Pilha de funcionamento do cucumber.


Gherkin

O padrão de escrita chamado Gherkin nasceu da necessidade  de expressar de forma clara o comportamento esperado das funcionalidades que os stakeholders esperam do sistema utilizando exemplos.

“O Gherkin fornece uma estrutura leve para documentar os exemplos de comportamento que os stakeholders querem.”  

Mesmo o Gherkin podendo ser similar a uma linguagem de programação o seu principal objetivo é ser legível para humanos, isto significa que, é possível escrever testes automatizados que podem ser lidos como documentação tais como no exemplo a seguir:



Funcionalidade: Retirar dinheiro do caixa eletrônico

Cenário: Retirando dinheiro de uma conta com crédito
Dado uma conta contendo R$ 100,00
Quando eu requisito $ 49,00
Então o caixa deve liberar o valor de $ 49,00
E o saldo restante deve ser R$ 51,00

Exemplo de cenário escrito com Gherkin.

O Gherkin também foi traduzido para mais de quarenta idiomas bastando para isso utilizar as palavras-chave (em negrito no exemplo anterior) equivalentes no idioma desejado.


Formato e Sintaxe

Os arquivos Gherkin usam a extensão .feature e são salvos em texto plano, isto significa que eles podem ser lidos e editados por ferramentas simples. A estrutura de um arquivo Gherkin é estabelecida pelas seguintes palavras-chave (com seus equivalentes em português):

  • Feature, Funcionalidade.
  • Background, Contexto.
  • Scenario, Cenário.
  • Given, Dado.
  • When, Quando.
  • Then, Então.
  • And, E.
  • But, Mas.
  • *.
  • Scenario Outline, Esquema do Cenário.
  • Examples, Exemplos.

Para outro idioma basta utilizar as palavras equivalentes na linguagem desejada.



Feature (Funcionalidade)

Cada arquivo Gherkin começa com a palavra-chave Feature. Essa palavra-chave não afeta o comportamento do cucumber, ela apenas dá um lugar conveniente para escrever alguma documentação resumida a respeito do comportamento da funcionalidade que será descrita através dos testes.

Segue um exemplo da utilização dessa palavra-chave:

Funcionalidade: Esse é o título da funcionalidade
Essa é a descrição da funcionalidade que pode ocupar
diversas linhas.
É possível até mesmo colocar linhas em branco

De fato, tudo que esteja escrito até a próxima palavra-chave do Gherkin é incluído
na descrição.

Dentro de uma estrutura Gherkin válida a palavra-chave Feature deve obrigatoriamente ser seguida de uma das seguintes palavras-chave:

  • Scenario
  • Background
  • Scenario Outline

Scenario (Cenário)

Para representar o comportamento necessário, cada feature contém vários Scenarios. Cada Scenario é um único e concreto exemplo de como a funcionalidade em questão deve se comportar em um caso específico. Todos os Scenarios de uma Feature juntos definem o comportamento dessa Feature.
Todos os Scenarios seguem o seguinte padrão:

  1. Colocam o sistema em um estado específico.
  2. Realizam algum tipo de ação.
  3. Examinam o estado final.





Given, When, Then (Dado, Quando, Então)

No padrão Gherkin os estados, ações e asserções que ocorrem em um Scenario são representados pelas palavras-chave Given, When, Them, da seguinte forma:

Scenario:
Given <prepara o contexto>
When <executa uma ação ou evento>
Then <verifica o estado final>

Dessa  forma usa-se o  Given para preparar o contexto de onde o Scenario vai acontecer. Usa-se o When para interagir com o sistema de alguma forma e o Then para checar se o resultado dessa ação foi o esperado.

And, But (E, Mas)

Cada linha de um Scenario é conhecido como step (passo). É possível adicionar mais steps para cada Given, When ou Them utilizando-se And e But. Como no exemplo a seguir:

Cenário: Retirando dinheiro de uma conta com crédito
Dado uma conta contendo R$ 100,00
Quando eu requisito $ 49,00
Então o caixa deve liberar o valor de $ 49,00
E o saldo restante deve ser R$ 51,00

Essas palavras-chave não modificam o comportamento do Cucumber e existem simplesmente para promover a legibilidade dos Scenarios.

terça-feira, 9 de outubro de 2012

Teste de Software

E aí galera, tudo bem?!..
O primeiro post deve ser o mais difícil pelo tanto que estou sofrendo :') ....Enfim, decidi compartilhar aquilo que sei para que juntos possamos aprender cada vez mais! Esse primeiro post tem por objetivo fazer uma introdução praticamente acadêmica acerca do tema em geral e é um resumo de um dos capítulos do meu TCC.

Espero que gostem!


  \O/


Obs: Neste e nos próximos posts colocarei bonitinhas as referências assim que estruturar todas!


Enjoy!    




   Teste de Software




Com o passar do tempo a tecnologia da informação se fez cada vez mais presente em diversas atividades humanas, chegando a se  tornar crucial para o funcionamento de algumas áreas, o que levou se exigir cada vez mais qualidade dos softwares desenvolvidos [COSTA, 2004]. Sendo assim,a sociedade, atualmente, depende cada vez mais de sistemas computacionais de qualidade pois seu uso cresce de forma exponencial nos mais diversos setores desempenhando tarefas críticas. Se alguns sistemas de uso global deixarem de funcionar, aproximadamente 40% da população sofrerá as consequências deste problema [REED, 2000]. Entretanto, é possivel afirmar que apesar do quão significativos softwares são, uma grande parte dos projetos de software não possui uma estrutura sólida para implementação de métodos que venham a proporcionar a garantia da qualidade de seu produto final. Em uma pesquisa recente realizada pelo Ministério da Ciência e Tecnologia, cerca de 10% das empresas entrevistadas realizam algum teste de software no Brasil [MOLINARI, 2008].
“A qualidade é relativa. O que é qualidade para uma pessoa pode ser falta de qualidade para outra (G. Weinberg)”. Embora o conceito de qualidade aparente ser intuitivo, ao analisá-lo conclui-se que, na verdade, é complexo e defini-lo não é uma tarefa fácil. Segundo [CROSBY ,1992] a qualidade é a conformidade aos requisitos, um conjunto de características a serem satisfeitas em um determinado grau, de modo que o produto de software atenda às necessidades explícitas e implícitas de seus usuário  [ROCHA, 1994]. Conforme a ISO 9003-3-1991, qualidade é a totalidade de características e funções  de um produtos ou serviço que habilitem a satisfação de necessidades implícitas ou especificadas.Qualidade ainda, segundo a atual norma brasileira sobre o assunto (NBR ISO 8402), é a totalidade das características de uma entidade que lhe confere a capacidade de satisfazer às necessidades explícitas e implícitas. Entretanto, para a obtenção da qualidade em um produto final é necessária a compreensão de que esta é intimamente ligada a todo os segmentos do ciclo de desenvolvimento de um software e não somente a uma fase específica.
[MOLINARI,2008] afirma que existem três elementos que compõe e sustentam a qualidade de software, sendo estes a gerência de configuração, a gerência de requisitos e os testes.  A gerência de configuração aborda a gerência, controle e registro dos artefatos que compõe o software, incluindo todo o histórico de mudança deste. Já a gerência de requisitos trata da gerência, controle e registro das necessidades do sistema e dos usuários em todos os níveis, resultando no acompanhamento dos requisitos e suas mudanças. Já o segmento de teste vai desde o gerenciamento de  todo o planejamento, controle e até a execução dos mesmos.
Somente a existência de testes no processo de desenvolvimento de um  software  não é capaz  de creditar a este a característica qualidade, entretanto pode auxiliar no processo de controlá-la e garanti-la, aumentando o grau de confiabilidade do sistema. Segundo [HAROLD, 2000]  o processo de teste é executado para dar a  garantia de qualidade ao produto de software e é a mais popular estratégia de gerenciamento de risco [MOLINARI,2008]. Este procedimento faz-se necessário pois o desenvolvimento de sistemas de software envolvem uma série de atividades de produção em que oportunidades para injetar a faliabilidade humana são enormes.  [DEUTSCH, 1979] afirma que erros podem vir a ocorrer até no início do processo de desenvolvimento  onde os objetivos podem ser erroneamente ou imperfeitamente especificados, bem como em  estágios posteriores de projeto e desenvolvimento por causa da inabilidade humana de realizar e comunicar com imperfeição.
“Se eu tivesse oito horas para derrubar uma árvore, passaria seis afiando o meu machado. " Abraham Lincoln, esta frase pode representar  uma analogia ao processo de testes, uma vez esta atividade é uma das mais onerosas do processo de produção de um software. Estudos indicam que testes podem vir a consumir mais de 50% do custo de desenvolvimento de um software [HAROLD, 2000]. A rigor o teste de software que envolve vidas (por exemplo, controle de tráfego aéreo, monitoramento de reatores nucleares) pode custar de três a cinco vezes mais do que todos os outros passos de engenharia de software combinados [PRESSMAN, 2002]. Segundo um estudo realizado pelo National Institute of Stanandarts and Tecnology (NIST) durante o ano de 2002, somente no Estados Unidos, perdas causadas por erros em sistemas somaram cerca de  U$ 59.5 bilhões de dólares,  este estudo aponta ainda  que o investimento na melhoria da infra-estrutura para a área de testes geraria uma economia de U$ 22 bilhões ao país.
A figura 1 representa um gráfico de custo médio de um erro por etapas do processo de desenvolvimento. Ela representa o ciclo de custo de um defeito, demonstrando que ele cresce em uma proporção média de 10 vezes a cada etapa de desenvolvimento. Sendo assim um erro encontrado na especificação tem seu custo em 10 centavos de dólar para ser corrigido. Já para o caso do mesmo erro ser esquecido e ser detectado na fase de implantação o custo seria de 100 dólares (BARTIÉ, 2002).


                                             Fonte: Bartie Alexandre (2002)
Figura 1. Custo médio de um erro em Dólar 

Atividades de teste devem ser cuidadosamente planejadas para fornecer um nível adequado de confiabilidade, atingir aos requisitos, dentro das condições especificadas, contribuindo para qualidade de software, já que testes bem planejados são capazes de remover 60% dos defeitos de um programa [BOEHM e BASILI, 2001], isto aplica-se também ao fato de que um bom teste não é redundante. Na prática não se pode garantir que um software é perfeito, devido principalmente a complexidade envolvida em sua construção, entretanto a  não ou má execução deste processo em alguns projetos além de custos altíssimos podem acarretar em graves acidentes pondo em risco vidas humanas. Em novembro de 2000 no Instituto Nacional do Cancro da Cidade do Panamá, ao menos oito pacientes morreram e outros 20 foram afetados devido a uma falha no software de controle da máquina de raios, havia um  erro no cálculo na dose de radiação que um paciente deveria receber durante uma terapia radiológica. Um exemplo de fracasso de um projeto de orçamento astronômico, avaliado em U$ 300 milhões, ocorreu em  3 de dezembro de 1999, quando a sonda da NASA Mars Polar Lander desapareceu na tentativa de um pouso em Marte. O motivo da falha foi um bit inesperado no conjunto de instruções do programa que ocasionou um problema na “cama de pouso” da sonda [MOLINARI, 2008].
Testes de software são elementos críticos da garantia de qualidade de software e representam uma revisão final da especificação, projeto e geração de código [PRESSMAN, 2002]. A definição clássica de teste de software foi dada por Myers em 1979 em um dos primeiros trabalhos a estudar a fundo esta atividade, caracterizando-se como o processo de executar o programa com a intenção de encontrar erros, partindo da premissa de que se o objetivo dos testes fossem somente provar a boa funcionalidade de um aplicativo seriam encontrados menos defeitos em relação a outra frente de pensamento, uma vez que toda a energia do processo de testes seria direcionada apenas  na comprovação deste fato [BARTIÉ, 2002].  Conforme [DIJKSTRA et al, 1972], os testes podem mostrar apenas a presença de erros e não a sua ausência. Logo, a atividade de teste demonstra que as funções do software estão sendo executadas de acordo com as especificações, que os requisitos de desempenho foram cumpridos e que, por conseqüência, as informações geradas por ele são confiáveis.
Afirma [NEVES, 1999] que os testes tem por objetivo no início de cada fase do ciclo de desenvolvimento de um software, verificar se aquela etapa corresponde exatamente aos requisitos e definições da fase imediatamente anterior, para assim garantir que o produto encomendado e o gerado pela atividade de desenvolvimento do software sejam os mesmos, através dos diversos níveis de refinamento. A finalidade dos testes é expor defeitos latentes em um software antes de este ser entregue [SOMMERVILLE, 2003], consistindo em atividades que devem verificar se não existem erros na lógica no código ou de projeto, no fluxo de dados, na compreensão dos requisitos, no desempenho do sistema, na integração do sistema com outros componentes de software e hardware, para posteriormente interferir na presença do erro.
Desta forma, segundo [KOSCIANSKI, 2006], quando a atividade  de testes é planejada de maneira sistemática e rigorosa, pode ser utilizada como um dos parâmetros para estimar a confiabilidade e qualidade do software construído, testes servem em si, para aumentar o grau de confiança de que se esta construindo um produto com o comportamento desejado.Um bom caso de teste é aquele que tem uma elevada probabilidade de descobrir um erro e um teste bem sucedido é aquele que revela um erro ainda não descoberto. Conforme [LEWIS, 2004],o teste  "É uma estratégia popular para o gerenciamento de risco". O teste de software é usado para verificar que requisitos funcionais e não-funcionais foram devidamente implementados. Pyhäjärvi e Rautiainen (2004) mencionam que Testes de Software  também representa uma importante ferramenta na busca pela satisfação do cliente – um dos principais objetivos das pesquisas em sistemas de informação e pela melhoria do processo de desenvolvimento.
 Um princípio adotado ao se testar um software é o de Pareto, este afirma que para muitos fenômenos, 80% das consequências advém de 20% das causas, mostrando assim que  há um grande desiquilíbrio entre causas e efeitos, entre a relação de esforços e resultados. Conforme [KOSCIANSKI, 2006], em programação, pode ser aplicado ao afirmar de uma forma genérica que 20% dos componentes de software concentram 80% dos defeitos. A grande aplicabilidade deste princípio encontra-se no fato de ajudar a identificar o reduzido número de causas que estão muitas vezes por trás de grande parte dos problemas que ocorrem, sugerindo assim que o foco dos testes deve ser concentrado nos pontos mais frágeis do sistema. No processo de desenvolvimento de software todos os erros são erros humanos e, apesar do uso dos melhores métodos de desenvolvimento, das melhores ferramentas de suporte e de pessoal treinado, os erros permanecem presentes nos diversos produtos de software produzidos e lançados no mercado [HOWDEN, 1987].
 Como em qualquer outra fase do ciclo de desenvolvimento de um software, a fase de testes requer certos cuidados em relação à compreensão de seus princípios básicos antes de sua aplicação de fato. Segundo [MYERS, 1979] a tarefa de testar um software é extremamente criativa e intelectualmente desafiadora, portanto sugeriu princípios para condicionar a acrescentar sustentabilidade ao projeto de teste e execução destes, são elas: o programador deve evitar de testar seu próprio programa, deve-se inspecionar completamente os resultados de cada teste. A probabilidade da existência de mais erros em uma seção de um programa é proporcional ao número de erros já encontrados naquela seção, não se deve planejar nenhum caso de teste sob a suposição de que nenhum erro será encontrado e que uma parte extremamente necessária de um caso de teste é a definição de uma saída ou resultado esperado. [PRESSMAN, 2002] acrescenta outros seis princípios, sendo estes: [1] todos os testes devem ser relacionados aos requisitos do cliente, [2] os testes devem ser planejados muito antes do início do teste, [3] o princípio de Pareto se aplica ao teste de software, [4] teste completo não é possível, [5] o teste começa em nível de componente e prossegue “para fora”, [6] o teste e a depuração são atividades diferentes entretanto a depuração deve ser acomodada em qualquer estratégia de teste.
                 Conforme Crespo et al as dificuldades em se testar um software pode ser descrito da  seguinte maneira:

                     
Devido a algumas características próprias do software como flexibilidade para mudanças, complexidade e intangibilidade, o teste não é uma tarefa trivial. A dificuldade em testar software é caracterizada por alguns pontos importantes como: o teste de software é um processo caro; existe uma falta de conhecimento sobre a relação custo/benefício do teste; há falta de profissionais especializados na área de teste; existem dificuldades em implantar um processo de teste; há o desconhecimento de um procedimento de teste adequado; há o desconhecimento de técnicas de teste adequadas; há o desconhecimento sobre como planejar a atividade de teste; e finalmente,  a preocupação com a atividade de teste somente na fase final do projeto.(2004).


            Entretanto, a medida que é reduzida a complexidade da atividade de teste seu custo tende a diminuir. Sendo assim, no momento em que componentes que agregam características que tornam um software mais facilmente testável são utilizados seu grau de testabilidade evolui. A testabilidade consiste na facilidade em que um programa tem de ser testado. Segundo a [IEE,1990], a testabilidade fornece o grau de dificuldade que será encontrado para detectar o defeito, refere-se a quanto esforço será necessário para cada teste e se o software apresenta suas falhas no momento em que são inseridas as entradas de teste, caracterizando-se como um programa com alta testabilidade. Do modo contrário ocorre um software com baixa testabilidade possuindo tendencia a ocultas as falhas que deveriam ser detectadas durante os testes.
          As seguintes características levam a um software testável e devem ser levadas em conta quando ele é construído [ZADROSNY,2006],[PRESSMAN,2002]:
● Operabilidade: o software deve ser projetado e implementado com qualidade. ”Quanto melhor funciona, mais eficientemente  pode ser usado e testado”.
● Observabilidade: variáveis devem ser visíveis ou consultáveis durante a execução. “O que você vê é o que você testa.”
● Controlabilidade: variáveis devem poder ser controladas diretamente pelo engenheiro de teste. ”Quanto mais você pode controlar o software, mais o teste pode ser automatizado e otimizado”.
Decomponibilidade: módulos independentes podem ser testados independentemente. ”Controlando o escopo do teste, podemos isolar problemas mais rapidamente e realizar retestagem mais racionalmente”.
● Simplicidade: o conjunto de características deve ser o mínimo necessário para atender os requisitos. ”Quanto menos há a testar, mais rapidamente podemos testá-lo”.
● Estabilidade: o software se recupera bem de falhas. ”Quanto menos modificações, menos interrupções no teste”.
● Compreensibilidade: a documentação técnica é acessível instantaneamente, bem organizada, específica, detalhada e precisa.”Quanto mais documentação temos, mais racionalmente vamos testar”.

Conforme [ CRESPO et al,2004] e [KOSCIANSKI, 2006] existem, basicamente  duas técnicas de teste para qualquer produto que passa por engenharia. Sendo uma delas o  teste estrutural também conhecido como teste de caixa-branca, que caracteriza-se por ser uma técnica de teste que adota critérios para a geração dos casos de teste com a finalidade de identificar defeitos nas estruturas internas do software, através de situações que exercitem adequadamente todas as estruturas utilizadas na codificação. Nesta técnica , o conhecimento interno de um produto oferece informações para testar se todas as operações internas estão sendo realizadas conforme as especificações e se todos os componentes internos estão sendo exercitados corretamente [PRESSMAN, 2002]. Já a segunda forma, chamada de  teste funcional  também conhecida como teste de caixa preta, é técnica de teste que adota critérios para a geração dos casos de teste com a finalidade de garantir que os requisitos do software que foi construído sejam plenamente atendidos. Desta maneira, o conhecimento da função para que o produto foi projetado para efetuar oferece informações para realização de testes que demonstrem que cada função está plenamente operacional, ao mesmo tempo em que erros são procurados em cada uma das funções, conforme critérios [PRESSMAN, 2002].
A visão do objeto a ser testado afeta no modo em que os testes serão realizados, [PFLEEGER, 2004] complementa que se o objeto for visto de uma  perspectiva externa, o teste consistirá em alimentar o objeto com entradas e observar se as saídas correspondem com o esperado, e ao se observar o objeto a ser testado sob uma perspectiva interna, para planejar os casos de testes será utilizada a  estrutura do objeto. Deve-se levar em consideração também a fase de desenvolvimento em que o teste será aplicado, já que a técnica de teste direciona a escolha de critérios para geração de casos de teste que, ao serem executados, vão exercitar os elementos requeridos pela abordagem do teste. Portanto, para realizar esta atividade de teste deve-se utilizar técnicas de testes de software bem estabelecidas e estas devem ser realizadas de forma sistemática. [MYERS, 1979] sugere que uma estratégia de testes onde sejam combinadas as técnicas de caixa-branca e caixa preta, sendo os casos de teste estruturais  realizados como complementares aos testes funcionais, já que um número razoável de técnicas deve ser aplicado a ao teste de software, mas nenhuma isoladamente.
Segundo [KOSCIANSKI, 2006] os testes de caixa-branca são projetados em função da estrutura do componente e podem permitir a verificação mais precisa de comportamento do produto, permitindo ao testador focar sua atenção nos pontos mais importantes ou delicados do código de uma maneira mais precisa. Consistindo em uma abordagem de testes em que estes são derivados do conhecimento da estrutura e da implementação do software, permitindo  uma verificação mais precisa do produto [SOMMERVILLE, 2003]. Desta forma,  os dados de teste são dirigidos para examinar a lógica do programa, sem interesse nas especificações  do mesmo [LEWIS, 2000]. Este teste objetiva verificar se a estrutura interna da unidade está correta e para isso são realizados para  os caso de teste que visam percorrer todos os caminhos internos possíveis da unidade, abrangendo também,  a manipulação dos valores dos atributos, verifica os resultados quando loops são executados até o seu limite visando identificar os caminhos críticos de cada procedimento, entretanto percorrer todos os caminhos acaba tornando esta atividade complexa. Os casos de teste do tipo estrutural devem garantir que todos os caminhos independentes de um módulo tenham sido exercitados pelo menos uma vez, exercitando todas as decisões lógicas em seus lados verdadeiro e falso,executando assim  todos os ciclos nos seus limites dentro de seus intervalos operacionais desta forma exercitando as estruturas de dados internas.
            Grafos de fluxo caracterizam-se como  uma ferramenta de representação baseada em em nodos e arestas que são importantes ferramentas conceituais. [MYERS, 1979] e [PRESSMAN, 2002] definem que cada círculo, chamado de nó do grafo de fluxo representa um bloco de comandos que são executados sequencialmente e cada seta, chamada aresta, arco ou ligação, indica a transferência de controle entre segmentos e sempre deve terminar em um nó. A figura 1 apresenta a notação de um grafo de fluxo.
           
                                                            Figura 2 -Notação Grafo de Fluxo

Através deste tipo de ferramenta é possível aplicar uma estratégia de teste estrutural, denominada teste de caminho. [SOMMERVILLE, 2003] afirma que este tipo de teste tem por objetivo exercitar todo caminho distinto ou independente ao longo do código, ao menos uma vez, em algum teste. Afirma ainda que se todo caminho independente for executado, logo todas as afirmações do código devem ter sido executadas, e todas as declarações condicionais testadas para os casos verdadeiros e falsos. Entretanto, a execução de testes de caminhos para todas as combinações possíveis torna-se um objetivo impossível, já que há uma quantidade infinita de possíveis combinações de caminhos em programas com loops, então este tipo de teste é aplicado somente em caminhos básicos [MYERS, 1979]. Este método permite definir um conjunto mínimo de caminhos de execução  que garante o exercício de cada comando do programa ao menos uma vez durante a atividade de teste [PRESSMAN, 2002].
Outro exemplo de teste estrutural seria o teste de unidade que foca os módulos e componentes de um software. [MYERS, 1979] define este tipo de teste como um processo para testar subprogramas, individuais ou subrotinas em um programa, com o objetivo testar o funcionamento deste grupo de funções [SOMMERVILLE, 2003]. Este teste é realizado para mostrar que a unidade não satisfaz a sua especificação funcional e/ou que sua estrutura implementada não corresponde com a planejada. Os dados de teste são derivados conforme uma analise do código de um componente e, depois são utilizados como entrada para os testes, que são executados e geram saídas conforme ilustra a figura 3.

                                                              Figura 3.Teste unitário. 

Quando se faz uso do paradigma de teste caixa preta, que também pode ser chamado de teste comportamental ou teste de caixa fechada, se testa o programa sob a perspectiva do usuário, não levando em consideração sua estrutura interna ou meios de desenvolvimento. Segundo [SOMMERVILLE, 2003] seu comportamento do software ou parte dele só pode ser determinado através de estudos de suas entradas e suas respectivas saídas. A figura 4 é uma abstração desta técnica, e serve para elucidar demais conceituações sobre o assunto. Este tipo de teste  tem como objetivo medir a qualidade funcional de componentes de um sistema [MOLINARI, 2010], avaliando-os conforme seu comportamento externo. Quando se realiza um teste funcional está na verdade confrontando com o que se espera que o sistema vá fazer, ou seja, incluindo entrada de dados, processamento e resposta. [AGUIAR, 2007] afirma que este é o único tipo de teste possível para se aplicar no momento em que não se dispões do código-fonte, e ele é particularmente útil para encontrar problemas, como: funções incorretas ou omitidas, erros de interface, erros de comportamento ou desempenho e erros de iniciação [KOSCIANSKI, 2006].

Figura 4. Teste funcional
O teste de caixa-preta basea-se nos requisitos funcionais do software. Como não há conhecimento sobre a operação interna do programa, o avaliador se concentra nas funções que o software deve desempenhar [LOURENÇO, 2011]. A partir da especificação são determinadas as saídas esperadas para certos conjuntos de entrada de dados.[PEZZÈ, 2008] afirma que os testes funcionais tipicamente são usados como base para o projeto de casos de teste, já que estes devem começar como parte do processo de especificação de requisitos e continuar ao longo de cada nível de projeto e especificação de  interface sendo a  única técnica de projeto de teste com uma aplicabilidade tão ampla. Pode-se afirmar que testes de caixa preta podem ser aplicadados a qualquer descrição do comportamento de um software, sendo esta formal, parcial ou até mesmo informal, desde um módulo até abranger todo o sistema. Este tipo de caso de teste é mais barato para projetar e executar em relação ao modelo estrutural.
O teste de caixa preta exercita o programa sob diversas massas de dados preparadas anteriormente para provocar situações esperadas, validando aquela função ou estimulando a probabilidade de uma falha ocorrer através de situações mais propícias a esta condição. [HEINEBERG, 2008] afirma que o processo de teste funcional normalmente envolve a criação de cenários de testes para o uso na avaliação das funcionalidades da aplicação, validando se o que foi especificado foi implementado corretamente. Segundo [SOMMERVILLE, 2003] os testes funcionais são abordagens derivadas das especificações dos programas ou dos componentes, e possuem características de caixa-preta. [MYERS, 1979] define os testes funcionais com um processo na tentativa de encontrar divergências entre requisitos e programa. Conforme [PFLEEGER,2004] cita os testes relatados anteriormente concentravam-se nos componentes e integração dos mesmos, a abordagem dos testes funcionais ignora a estrutura interna do  programa e foca seu objetivo na funcionalidade do sistema, os testes funcionais tem como base os requisitos funcionais.
 Exemplo de um tipo de teste que aborda esta técnica chamada de particionamento por equivalência, também conhecido por classes de equivalência, é um método que divide o domínio de entrada  de um programa em classes de dados  a partir das quais  podem ser criados casos de teste. Segundo [PRESSMAN, 2011] um caso de teste ideal descobre sozinho uma classe de erros que poderia de outro modo, requerer que fossem executados muitos casos de teste até que o erro geral aparecesse. O projeto de casos de teste para particionamento de equivalência é baseado numa avaliação das classes de equivalência para uma certa condição de entrada, desta forma eliminando os casos de teste redundantes. Outro exemplo de técnica de teste de caixa preta é o teste baseado em modelo que faz uso de informações contidas no modelo de requisitos como base para a geração de casos de teste.Uma abordagem mais realista para o teste de caixa-preta é escolher um subconjunto de entradas que maximize a riqueza do teste.

As atividades de Teste de Software ainda podem ser divididas de acordo com a fase do desenvolvimento e a abordagem empregada, seja procedimental ou orientada a objeto (OO), conforme Beizer (1990) e Maldonado e Fabbri (2001). As fases são:

 Teste de unidade – aplicado a sub-rotinas e funções (na abordagem procedimental) e a métodos (na abordagem OO);
 Teste de integração – aplicado à relação entre dois ou mais subsistemas (na abordagem procedimental) e à classe, cluster, componente e subclasse (na abordagem OO); e
 Teste de sistema – na abordagem procedimental e na OO, é aplicado para verificação de se o sistema está de acordo com as especificações.
  

Este texto apresentou uma visão geral sobre qualidade e testes de software, caracterizando os objetivos desta fase e as divisões que permeiam as técnicas de teste . Conclui-se que a  área de teste de software vive em constante transformação, que segue de perto o contínuo crescimento do mercado de desenvolvimento de software. Na mesma medida em que ocorrem mudanças tecnológicas na área de desenvolvimento, também se torna necessária uma adequação das praticas de teste.