Léna Bamouh
Nantes Université
Erwan Bousse
Nantes Université
MoDeVVa 2024, Linz, Austria
Léna Bamouh
Nantes Université
Erwan Bousse
Nantes Université
Efficiently identifying relevant combinations of input arguments for a given System Under Test (SUT), and use combinations to design test cases.
Define a combinatorial model, i.e. an abstraction of the input domain, and use it to generate a set of abstract test cases (ATCs) satisfying a coverage criterion.
isPalindrome
(1)Method signature and example calls
/**
* Check whether a word is a palindrome.
*
* @param word The word to check.
* @return true if word is a palindrome, false otherwise.
* @throws IllegalArgumentException if word is either null, empty,
* contains a special character,
* or contains a digit
**/
public static boolean isPalindrome(String word) throws IllegalArgumentException
isPalindrome("anna")
returns true
,
isPalindrome("john")
returns false
,
isPalindrome("hello!")
throws an IllegalArgumentException
.
isPalindrome
(2)Definition of a combinatorial model
Use of Microsoft’s PICT language to define a combinatorial model
5 abstract parameters to cover relevant word
contents (‘_
’ denotes "no value")
2 constraints to forbid illegal combinations:
if word
is null
, it cannot be empty,
if word
is empty, it cannot contain characters nor be a palindrome.
isPalindrome
(3)Generation of abstract test cases
Run pict
to generate combinations, choosing a pairwise coverage criterion,
6 abstract test cases obtained as a result :
isPalindrome
(4)Manual definition of concrete test cases
A Concrete Test Case (CTC) must be manually defined for each ATC :
a valid concrete value must be defined for each input parameter (here, word
)
an oracle must be defined, based on the SUT specification
↓
isPalindrome
(5)Manual implementation of a test script
A test script must be manually implemented for all CTCs.
↓
@Test
void testIsPalindromeCTC5() {
assertThrows(IllegalArgumentException.class, () -> isPalindrome("!anna!"));
}
Manual work still required for CTCs definition (including input data selection and oracle definition) and test script implementation
Can be significant with important combinatorial complexity (ie. many combinations)
A first end-to-end model-driven approach to automatize the concretization of abstract test cases (ATCs).
Oracle-enhanced PICT combinatorial model
Oracles must be manually defined when defining CTCs
Extend the combinatorial model with an Oracle
variable that can be:
return<Java expression>
(check the returned value)
throws<Java exception type>
(check that an exception is thrown)
undefined
(means the oracle must be manually defined)
Add constraints specifying what oracles should be generated in combinations
Example with isPalindrome
− oracle definition in the PICT model
Cannot (yet) express oracles that depend on input parameters
Example with isPalindrome
− resulting (oracle-enhanced) ATCs
Combinatorial metamodel and ATC metamodel
Data generator
Input data must be manually defined based on ATCs
Implement a data generator that must comply with the following criteria:
Must be a public and static Java method,
Must have one String
parameter per parameter of the combinatorial model,
The return type must match the input parameters of the tested Java method (in a List
is multiple input parameters).
Example with isPalindrome
− constraint programming (CP) data generator
data generator signature:isPalindrome
public class PalindromeDataGenerator {
public static String generateData(String nullWord,
String emptyWord, String specialCharWord,
String digitCharWord, String generatePalindrome) { … }
}
data generator helper method (CP-based using Choco):isPalindrome
private static void addPalindromeConstraint(Model model, IntVar[] word) {
for (int i = 0; i < (word.length + 1) / 2; i++)
model.arithm(word[i], "=", word[word.length - 1 - i]).post();
}
CTC metamodel
Example with isPalindrome
− resulting CTC model
Example with isPalindrome
− resulting test script snippets
Java code generated for CTC3
@Test
void testIsPalindromeCTC3() throws IllegalArgumentException {
actualValue = isPalindrome("baaaa");
assertEquals(false, actualValue);
}
Java code generated for CTC4
@Test
void testIsPalindromeCTC4() {
assertThrows(IllegalArgumentException.class,
() -> isPalindrome("a0a0a"));
}
Prototype implemented as a Java annotation processed by a Maven plugin,
Test scripts are automatically generated when compiling the project with Maven.
Example of GenerateTest
annotation used on isPalindrome
@GenerateTest(
PICTInputName = "palindrome_model.pict",
generatorPackage = "data.generation.PalindromeDataGenerator",
generatorMethod = "generateData")
public static boolean isPalindrome(String word) throws IllegalArgumentException { … }
Research questions and experimental setup
How does the combined effort of implementing a data generator and extending the combinatorial model with an oracle definition compare to the effort of implementing test scripts manually?
How does this comparison vary with combinatorial complexity, e.g., when changing the target coverage criterion?
5 example methods from the field of software testing (isPalindrome
, getTriangleType
, getDaysInMonth
, findCommand
, validatePasswordStrength
)
Effort measured in lines of code (LoC)
Resulting LoC measured on the 5 case studies
isPalindrome
, getTriangleType
, and getDaysInMonth
are not "breaking even" (ie. more effort required than coding manually),
findCommand
"breaks even" with 3-wise,
validatePasswordStrength
"breaks even" with both pairwise and 3-wise criteria.
Approach "worth it" when the combinatorial complexity is high enough.
Combinatorial testing requires manual work in its last stages (ie. concretization)
A first end-to-end model-driven approach to automatize concretization with data generation (eg. using constraint programming)
More complex input data (eg. nested objects)
More complex oracles in the combinatorial models
Other types of data generators (eg. AI-based)