Mutação de Interface Interface Mutation: An Approach for Integration Testing Marcio E. Delamaro José C. Maldonado Aditya P. Mathur Erros de Integração Ocorrem quando um valor incorreto é passado a uma unidade. Considere um programa P e um caso de teste t. Suponha que em P existem unidades F e G e que F faz chamadas para G. Considere S1(G) a n-tupla de valores passados para G e S0(G) a n-tupla de valores retornados de G. Quando se executa P com o caso de teste t, um erro de integração é identificado na chamada de F para G quando: Erros de Integração Tipo 1: entrando em G, S1(G) não têm os valores esperados e estes valores causam uma falha antes de retornar de G. Tipo 2: entrando em G, S1(G) não têm os valores esperados e estes valores levam a um S0(G) incorreto, que por sua vez causa uma falha depois de retornarem de G. Tipo 3: entrando em G, S1(G) têm os valores esperados, mas valores incorretos em S0(G) são produzidos dentro de G e estes valores incorretos causam uma falha após retornarem de G. Erros de Integração Por exemplo na linguagem C: S1(G): A n-tupla de valores de entrada em uma chamada de função G é determinada por: os valores dos parâmetros de entrada usados na chamada da função as variáveis globais usadas em G S0(G): A n-tupla de valores de saída em uma chamada de função G é determinada por: os valores dos parâmetros de saída usados pela função as variáveis globais usadas em G os valores retornados por G Erros de Integração Mutação de Interface Para explicar Mutação de Interface foi examinada a natureza dos dados trocados entre as unidades. Em uma chamada de uma unidade F para uma unidade G, foram identificados quatro modos em que dados podem ser trocados entre a unidade que chama e a unidade chamada: Mutação de Interface Dados podem ser passados para G através de parâmetros de entrada (passagem de parâmetros por valor) Dados podem ser passados para G e/ou retornados para F através de parâmetros de entrada/saída (passagem de parâmetros por referência) Dados podem ser passados para G e/ou retornados para F através de variáveis globais Dados podem ser passados para F através de valores de retorno Mutação de Interface O primeiro passo é perturbar o sistema Este processo de perturbação fazendo uma mudança sintática simples também conhecido como mutação e o programa perturbado como um mutante Aplicação de Mutação de Interface somente nos pontos de interface (ou conexões) entre as unidades Mutação de Interface As variáveis ou expressões que influenciam os quatro tipos de troca de dados são os candidatos para possíveis mutações. A transformação é determinada por um operador de Mutação de Interface. Depois dos mutantes gerados, os próximos passos na Mutação de Interface são os mesmos do teste de mutação tradicional: executar os mutantes avaliar conjunto de testes adequado e decidir quais os mutantes equivalentes Mutação de Interface Exemplo: Considere um operador de Mutação de Interface OP(v) que perturba uma variável v. O testador pode escolher por exemplo a conexão S1-S3 para testar, neste caso OP é aplicado sobre variáveis desta conexão. Mutação de Interface Introduzindo erros tipo 1 e 2: Para introduzir estes erros, OP pode mudar a entrada da unidade S3. Esta entrada pode ser usada dentro de S3 e causar um retorno incorreto para a unidade S1. OP pode ser aplicado de duas maneiras: primeiro, na passagem de parâmetros por valor, OP pode ser aplicado na chamada da unidade S3 que ocorre dentro de S1; segundo, na passagem de parâmetros por referência, OP pode ser aplicado nas referências que serão retornadas, dentro de S3. Mutação de Interface Se o primeiro caso for usado, OP é aplicado no arg linha 5: máximo 1 mutante. 1 caso de teste é necessário. dois dos três usos do parâmetro x linhas 14,16 e 18 não testados. Se o segundo caso for usado: um mutante para cada uso do parâmetro x um caso de teste é requerido para distinguir cada um dos 3 mutantes. cada referência para x seja testada pelo menos uma vez. Mutação de Interface Teste de Interface versus Testes de Unidade É importante notar que um operador de Mutação de Interface é focado no teste de interação entre duas unidades e não no teste de uma unidade. Por exemplo: suponha que conexões S3–S4 vão ser testadas e o operador OP produz um mutante dentro da unidade S4. Estes mutantes podem ser distinguidos por casos de testes que exercitem chamadas para S4 feitas de dentro de S2, de forma que a conexão que desejo não seja testada. Este exemplo aponta para uma sutil diferença entre teste de unidade como uma mutação tradicional em relação à Mutação de Interface. Mutação de Interface Teste de Interface versus Testes de Unidade Quando o operador de Mutação de Interface é aplicado dentro de uma função chamada Precisamos alterar a condição de Alcançabilidade para: Condição de Alcançabilidade para Mutação de Interface: o caso de teste executa um caminho que alcança o ponto onde a mutação foi inserida (em relação à conexão A-B). Operadores de MI para a Linguagem C Uma unidade em C função que implementa algum serviço através de uma interface Para uma função F, esta interface é definida por parâmetros formais, variáveis globais que ele acessa, e o código que ela implementa. A conexão entre unidades são definidas por chamada de funções. Operadores de Mutação de Interface e operadores de Mutação tradicional A idéia em ambos é produzir sutis diferenças entre o estado do programa original e do mutante. Operadores de MI para a Linguagem C A função F que possui duas chamadas para a função G: Operadores de MI para a Linguagem C G’ é a função criada pela aplicação de um operador de Mutação de Interface dentro da função G. Existem dois mutantes diferentes para considerar quando a conexão F-G for testada. O primeiro é criado trocando a primeira chamada de G por G’ e o segundo é criado trocando a segunda chamada de G por G’ Dois grupos de Operadores de MI são definidos considerando a conexão F-G operadores do primeiro grupo são aplicados dentro do corpo da função G Operadores do segundo são aplicados na chamada para G dentro de F Operadores de MI para a Linguagem C Grupo I – Operadores Aplicados dentro da Função Chamada. Estes operadores causam mudanças nos valores de entrado e/ou saída de funções. Para a aplicação destes operadores é necessário identificar o lugar de onde a função foi chamada. Mecanismo que habilite ou desabilite a mutação dependendo ou não de onde G é chamada de F (PROTEUM/IM) Quando a P(B): de B. G(B): L(B): E(B): C(B): conexão A-B irá ser testada: Conjunto que inclui os parâmetros formais Conj. Conj. Conj. Conj. Variáveis globais acessadas por B. Variáveis declaradas em B (locais). Variáveis globais não acessadas em B. Constantes usadas em B. Operadores de MI para a Linguagem C Grupo I Operadores de Substituição Direta de Variáveis Operadores de Substituição Indireta Operadores de Variáveis de Incremento e Decremento Inserção de Operadores Unários Operadores de Retorno Operadores de Cobertura Operadores de MI para a Linguagem C Grupo II – Operadores Aplicados dentro da Função que está fazendo a Chamanda. Dado uma conexão A-B são aplicados na instrução onde A chama B e sobre estes argumentos da chamada Quando um argumento é uma expressão são aplicados sobre a expressão inteira não sobre os objetos da expressão como variáveis ou constantes Operadores de MI para a Linguagem C Grupo I Substituição de Argumentos Troca de Argumentos Eliminação de Argumentos Inserção de Operadores Unários Exclusão de Chamada de Função Operadores de MI para a Linguagem C Comentários sobre Operadores de MI O mais importante é tornar o uso de teste de mutação possível de se aplicar e eficaz A aplicação de um conjunto inteiro de operadores pode criar um largo número de mutantes resultando em tempo impossível para se executar e avaliár É necessário permitir o testador ajustar o conjunto de operadores para uma aplicação especifica de acordo com os requisitos e custo Uma maneira de customizar a aplicação destes operadores é aplicar ‘restrições de mutação’: Aleatoriamente, selecionar uma pequena porcentagem do conjunto de muntantes. Aplicar somente um conjunto restrito de operadores Operadores de MI para a Linguagem C Comentários sobre Operadores de MI Estas restrições, reduzem significativamente o número de mutantes sem perda na eficácia da detecção de erros. A decisão de gerar um conjunto reduzido de mutantes ou aplicar todos os operadores e gerar todos os mutantes sempre depende dos requisitos de teste e do quão critica é a aplicação Os operadores do Grupo II tendem a gerar menos mutantes que os do Grupo I. Isso porque a complexidade para os operadores do Grupo II é proporcional ao número de parâmetros da unidade chamada. A complexidade para os ops. do Grupo I é proporcional ao nro. de variáveis (locais ou globais) acessadas na unidade chamada vezes o número de acessos para estas variáveis. Operadores de MI para a Linguagem C Comentários sobre Operadores de MI Provavelmente o problema de equivalência de mutantes é mais fácil sobre os gerados pelos operadores do Grupo II. Assim estes operadores são bons candidatos para gerar um conjunto inicial de mutantes para o inicio da avaliação do conjunto de testes. Apesar disso, operadores do Grupo I tem mais diversas características que operadores do Grupo II permitindo testar as interfaces de maneira mais completa, mas precisando de um maior custo.