Introduction
Most of this article is about "program mutation", in which the program is modified. A more general definition of ''mutation analysis'' is using well-defined rules defined on syntactic structures to make systematic changes to software artifacts.Paul Ammann and Jeff Offutt. Introduction to Software Testing. Cambridge University Press, 2008. Mutation analysis has been applied to other problems, but is usually applied to testing. So ''mutation testing'' is defined as using mutation analysis to design new software tests or to evaluate existing software tests. Thus, mutation analysis and testing can be applied to design models, specifications, databases, tests, XML, and other types of software artifacts, although program mutation is the most common.Overview
Tests can be created to verify the correctness of the implementation of a given software system, but the creation of tests still poses the question whether the tests are correct and sufficiently cover the requirements that have originated the implementation. (This technological problem is itself an instance of a deeper philosophical problem named " Quis custodiet ipsos custodes?" Who will guard the guards?") The idea behind mutation testing is that if a mutant is introduced, this normally causes a bug in the program's functionality which the tests should find. This way, the tests are tested. If a mutant is not detected by the test suite, this typically indicates that the test suite is unable to locate the faults represented by the mutant, but it can also indicate that the mutation introduces no faults, that is, the mutation is a valid change that does not affect functionality. One (common) way a mutant can be valid is that the code that has been changed is "dead code" that is never executed. For mutation testing to function at scale, a large number of mutants are usually introduced, leading to the compilation and execution of an extremely large number of copies of the program. This problem of the expense of mutation testing had reduced its practical use as a method of software testing. However, the increased use of object oriented programming languages andGoals
The goals of mutation testing are multiple: * identify weakly tested pieces of code (those for which mutants are not killed) * identify weak tests (those that never kill mutants) * compute the mutation score, the mutation score is the number of mutants killed / total number of mutants. * learn about error propagation and state infection in the programHistory
Mutation testing was originally proposed by Richard Lipton as a student in 1971,Mutation 2000: Uniting the OrthogonalMutation testing overview
Mutation testing is based on two hypotheses. The first is the ''competent programmer'' hypothesis. This hypothesis states that competent programmers write programs that are close to being correct. "Close" is intended to be based on behavior, not syntax. The second hypothesis is called the ''coupling effect''. The coupling effect asserts that simple faults can cascade or ''couple'' to form other emergent faults.A. Jefferson Offutt. 1992. Investigations of the software testing coupling effect. ACM Trans. Softw. Eng. Methodol. 1, 1 (January 1992), 5-20.A. T. Acree, T. A. Budd, R. A. DeMillo, R. J. Lipton, and F. G. Sayward, "Mutation Analysis," Georgia Institute of Technology, Atlanta, Georgia, Technique Report GIT-ICS-79/08, 1979. Subtle and important faults are also revealed by higher-order mutants, which further support the coupling effect.Yue Jia; Harman, M., "Constructing Subtle Faults Using Higher Order Mutation Testing," Source Code Analysis and Manipulation, 2008 Eighth IEEE International Working Conference on , vol., no., pp.249,258, 28-29 Sept. 2008Maryam Umar, "An Evaluation of Mutation Operators For Equivalent Mutants," MS Thesis, 2006Smith B., "On Guiding Augmentation of an Automated Test Suite via Mutation Analysis," 2008Polo M. and Piattini M., "Mutation Testing: practical aspects and cost analysis," University of Castilla-La Mancha (Spain), Presentation, 2009Anderson S., "Mutation Testing", the University of Edinburgh, School of Informatics, Presentation, 2011 Higher-order mutants are enabled by creating mutants with more than one mutation. Mutation testing is done by selecting a set of mutation operators and then applying them to the source program one at a time for each applicable piece of the source code. The result of applying one mutation operator to the program is called a ''mutant''. If the test suite is able to detect the change (i.e. one of the tests fails), then the mutant is said to be ''killed''. For example, consider the following C++ code fragment:&&
with , ,
and produce the following mutant:
a = 1
and b = 0
would do this.
# The incorrect program state (the value of 'c') must ''propagate'' to the program's output and be checked by the test.
These conditions are collectively called the ''RIP model''.
''Weak mutation testing'' (or ''weak mutation coverage'') requires that only the first and second conditions are satisfied. ''Strong mutation testing'' requires that all three conditions are satisfied. Strong mutation is more powerful, since it ensures that the test suite can really catch the problems. Weak mutation is closely related to Mutation operators
To make syntactic changes to a program, a mutation operator serves as a guideline that substitutes portions of the source code. Given that mutations depend on these operators, scholars have created a collection of mutation operators to accommodate different programming languages, like Java. The effectiveness of these mutation operators plays a pivotal role in mutation testing. Many mutation operators have been explored by researchers. Here are some examples of mutation operators for imperative languages: * Statement deletion * Statement duplication or insertion, e.g.goto fail;
* Replacement of Boolean subexpressions with ''true'' and ''false''
* Replacement of some arithmetic operations with others, e.g. +
with *
, -
with /
* Replacement of some Boolean relations with others, e.g. >
with >=
,
and <=
* Replacement of variables with others from the same scope (variable types must be compatible)
* Remove method body.
These mutation operators are also called traditional mutation operators.
There are also mutation operators for object-oriented languages, for concurrent constructions, complex objects like containers, etc.
Types of mutation operators
Operators for containers are called ''class-level'' mutation operators. Operators at the class level alter the program's structure by adding, removing, or changing the expressions being examined. Specific operators have been established for each category of changes. For example, the muJava tool offers various class-level mutation operators such as Access Modifier Change, Type Cast Operator Insertion, and Type Cast Operator Deletion. Mutation operators have also been developed to perform security vulnerability testing of programs. Apart from the ''class-level'' operators, MuJava also includes ''method-level'' mutation operators, referred to as traditional operators. These traditional operators are designed based on features commonly found in procedural languages. They carry out changes to statements by adding, substituting, or removing primitive operators. These operators fall into six categories: ''Arithmetic operators'', ''Relational operator''s, ''Conditional operators'', ''Shift operators'', ''Logical operators'' and ''Assignment operators''.Types of mutation testing
There are three types of mutation testing;Statement mutation
Statement mutation is a process where a block of code is intentionally modified by either deleting or copying certain statements. Moreover, it allows for the reordering of statements within the code block to generate various sequences. This technique is crucial in software testing as it helps identify potential weaknesses or errors in the code. By deliberately making changes to the code and observing how it behaves, developers can uncover hidden bugs or flaws that might go unnoticed during regular testing. Statement mutation is like a diagnostic tool that provides insights into the code's robustness and resilience, helping programmers improve the overall quality and reliability of their software. For example, in the code snippet below, entire 'else' section is removed:Value mutation
Value mutation occurs when modification is executed to the parameter and/or constant values within the code. This typically involves adjusting the values by adding or subtracting 1, but it can also involve making more substantial changes to the values. The specific alterations made during value mutation include two main scenarios: Firstly, there's the transformation from a small value to a higher value. This entails replacing a small value in the code with a larger one. The purpose of this change is to assess how the code responds when it encounters larger inputs. It helps ensure that the code can accurately and efficiently process these larger values without encountering errors or unexpected issues. Conversely, the second scenario involves changing a higher value to a smaller one. In this case, we replace a higher value within the code with a smaller value. This test aims to evaluate how the code handles smaller inputs. Ensuring that the code performs correctly with smaller values is essential to prevent unforeseen problems or errors when dealing with such input data. For example:Decision mutation
Decision mutation testing centers on the identification of design errors within the code, with a particular emphasis on detecting flaws or weaknesses in the program's decision-making logic. This method involves deliberately altering arithmetic and logical operators to expose potential issues. By manipulating these operators, developers can systematically evaluate how the code responds to different decision scenarios. This process helps ensure that the program's decision-making pathways are robust and accurate, preventing costly errors that could arise from faulty logic. Decision mutation testing serves as a valuable tool in software development, enabling developers to enhance the reliability and effectiveness of their decision-making code segments. For example:See also
*References
{{Software testing Software testing