Nesse vídeo eu vou falar sobre teste de mutação. Esse critério que complementa outros critérios funcionais e estruturais, utiliza conceito pouco diferente para que possamos criar os nossos casos de teste. O teste de mutação é critério baseado defeitos ou seja, utiliza conhecimento sobre defeitos típicos que podem ocorrer ao escrevermos programa para nos ajudar a criar bons conjuntos de testes. Eu gosto de usar o seguinte cenário para explicar o que é teste de mutação. Então imagine que você foi trabalhar uma empresa e seu chefe lhe dá uma primeira tarefa. Você deve implementar programa qualquer mas como você é novo na empresa, ele quer ver também como você testou seu programa para se certificar de que ele funciona. Certo? Bom, passado alguns dias ou meses, você retorna feliz da vida com o programa pronto e com o conjunto de teste que mostra que o seu programa realmente funciona. Bom, obviamente a primeira coisa que o seu chefe faz é pegar o seu programa e executá-lo com o conjunto de teste que você trouxe junto. Obviamente também seu programa funciona muito direitinho, e você fica orgulhoso afinal, você já tinha feito esse processo várias vezes para ter certeza que tudo funciona, mas aí você nota que seu chefe sem qualquer motivo aparente, afinal chefe não precisa de motivos, começa a mexer no código que você escreveu. Por exemplo, ele muda comando que está aqui vermelho que dizia ''if A maior do que B'' para ''A maior ou igual a B'', e aí executa os mesmos casos de testes com o programa modificado. A pergunta que eu faço é o seguinte: o que você espera que aconteça? Bom, muito bem, você deve ter respondido: "espero que o programa do meu chefe falhe com algum caso de teste". Sim, tem razão afinal, se o seu programa está correto, e o seu chefe é programa diferente do seu então ele tem que falhar algum dos seus casos de teste. Mas e se isso não acontecer? Bem, seu isso não acontecer é porque o seu conjunto de testes é ruim, ele não consegue exercitar o código de uma maneira completa, ou outras palavras, ele não consegue revelar aquele defeito que o seu chefe inseriu no seu programa. Então essa é toda a ideia do teste de mutação. Mas vez de fazermos isso uma única vez a gente faz isso várias vezes. Criamos vários programas que chamamos de mutantes ou seja, que possuem pequenas modificações relação ao programa sendo testado. Os casos de teste têm que mostrar que o seu programa apresenta sempre resultado correto, e que cada dos mutantes alguma vez apresenta resultado incorreto. Quando caso de teste consegue distinguir o comportamento de mutante do programa original dizemos que esse mutante está morto. Portanto, o objetivo que a gente tem com o teste de mutação é achar conjuntos de teste que matem todos os mutantes que foram criados. Mas e aí dado conjunto de testes, como que a gente faz para saber se esse conjunto é bom ou ruim? Bom, o que a gente faz é simples. Vamos contar quantos mutante ele consegue matar. Quanto mais mutantes mortos melhor é o conjunto de testes. Então por exemplo vamos supor que foram produzidos 110 mutantes, e que meu conjunto de teste matou 60 deles. A gente tem então uma medida que a gente chama de score de mutação que indica qual é a taxa de mutantes mortos. Ou seja, é o número de mutantes mortos divididos pelo número total de mutantes. Nesse caso por exemplo teve score de 0,55 ou 55% dos mutantes foram mortos pelo meu conjunto de casos de teste. Bom, uma outra pergunta importante é como são gerados os mutantes? Bom, os mutantes são criados baseados operadores de mutação, que são regras que definem quais alterações devem ser feitas para que os mutantes sejam criados. Esses operadores estão fortemente ligados com a linguagem na qual o programa que eu estou testando foi escrito. Eles buscam, explorar todas as características e estruturas da linguagem. Por exemplo, para a linguagem C foram definidos quatro classes de operadores. São aquele que alteram comandos inteiros do programa, aqueles que alteram operadores da linguagem, que alteram variáveis e que alteram constantes. Então, para cobrir todas essas estruturas que existem na linguagem, existem pelo menos 75 operadores de mutação projetados para essa linguagem. Uma consequência disso é que se usarmos todos os operadores de mutação o número de mutantes tende a ser bastante grande, mesmo para programas simples. Bom, eis aqui exemplo de operador de mutação. É o operador que a gente chama de ORRN ou, o que faz a troca de operador relacional por outro operador relacional. Por exemplo, eu tenho dentro do meu comando maior, operador maior, e ele vai se trocado por menor, maior igual, menor, igual ou diferente. Certo? Então, ele cria os mutantes trocando esses operadores por outros operadores relacionais. Então, esse exemplo desse programa é a implementação do Bubble Sort que a gente conhece, e ele utiliza operadores relacionais vários pontos dessa função, mas eu vou olhar especificamente nesse ponto que está marcado aqui nesse círculo vermelho. Para esse ponto a gente vai ter cinco mutantes ou seja, o operador vai ser aplicado cinco vezes para criar cinco mutantes diferentes. Esse é deles, que o maior foi trocado pelo maior e igual, esse é o segundo, esse é outro deles o terceiro, esse é o quarto, e esse então é o quinto. São cinco mutantes criados a partir desse ponto. Muito bem, se eu pegar o programa original e esse mutante que eu estou criando aqui, gerado pelo ORRN com esse dado de entrada ou seja, vetor que tem os valores dois, três, quatro e cinco, a gente vai ver que o resultado apresentado pelo mutante é diferente do resultado do programa original. Isso quer dizer que esse mutante foi morto por esse dado de teste. Então eu já achei dado de teste que mata esse mutante. Preciso fazer então a mesma coisa para todos os outros mutantes que foram gerados por esse programa. Mas nem tudo é tão bonito assim no teste de mutação. Existem alguns mutantes que são mutantes muito chatos como esse que está sendo mostrado nessa telinha aqui. Veja que se a gente executar esse mutante com esse dado de teste, com essa entrada ele não morre, e na verdade ele não morre com nenhum caso de teste. Ou seja, não existe uma forma de matar esse mutante. Então ele é chato porque nem sempre é fácil de descobrir se mutante ele pode ser morto, ou se ele nunca vai poder ser morto. Que é que a gente chama de mutante equivalente. O testador é que tem que perder o tempo dele para analisar os mutante e descobrir se ele pode ser morto ou se ele deve ser descartado simplesmente pelo fato dele ser mutante equivalente. Muito bem, como a gente precisa desconsiderar os mutante equivalentes já que não podem ser mortos ou seja, não servem para selecionar casos de testes o score de mutação ele fica dessa forma que eu estou mostrando aqui agora. No cálculo a gente precisa saber quantos são os mutante equivalentes e aí então temos que excluir esses mutante para termos número correto no score de mutação. No nosso exemplo então a gente vai ter se eu tenho lá dez mutantes equivalentes, se eu vou ter 60 mutantes mortos dividido pelos 100 mutantes que não são equivalentes, e eu tenho score de mutação de 60%. E então aqui resuminho de como a gente aplica o teste de mutação. Eu tenho conjunto de teste, gero mutantes e executo o meu programa com o conjunto de teste. Se uma falha acontece então eu achei defeito no meu programa e eu preciso corrigi-lo. Caso contrário, eu executo mutantes com o conjunto de teste, e analiso com os que sobram vivos posso identificar mutantes como equivalentes, ou adicionar novos casos de teste para matar os não aquivalentes. E assim o teste procegue até que a gente tenha matado todos os mutantes que não são equivalentes. No próximo vídeo eu vou mostrar para vocês como é que a gente aplica na prática o teste de mutação utilizando uma ferramenta que dá apoio a esse critério.