I have an algorithm that checks which String in a given List of Strings matches the most with another given string, it goes as-
String calcMostMatching(String mainStr, List<String> strings) {
String mostMatching = null;
if (strings.size() == 1) {
// if size is 1, return the only present string.
mostMatching = strings.get(0);
} else {
// This algorithm also works for size() == 1 but I don't
// want to use it when size() == 1
char[] charArrayOfMainStr = mainStr.toCharArray();
for (String str : strings) {
char[] charArrayOfStr = str.toCharArray();
int min = Math.min(charArrayOfMainStr.length, charArrayOfStr.length)
for (int i = 0; i < min; i ) {
int count = 0;
if (charArrayOfMainStr[i] == charArrayOfStr[i]) {
count ;
} else {
break;
}
}
// Now I store the value of count for
// each string but let us say a method like str.setCounter(count);
}
// Now I will iterate over the loop to check the mostMatchingString
mostMatching = strings.get(0);
for (int i = 1; i < strings.size(); i ) {
if (mostMatching.getCounter() < strings.get(i).getCounter()) {
mostMatching = strings.get(i);
}
}
}
return mostMatching;
}
Now pit test fails on-
if (strings.size() == 1) {
mostMatching = strings.get(0);
}
Saying replaced strings.size() == 1
with false
but still all the test cases pass. Yes, all the test cases will pass but I want to reduce time complexity when the list has only one value.
If you are not aware of pit testing, can someone help me to modify this algorithm so it doesn't go inside the loop when size() == 1
other than what I have done.
Note- This is not the real code, I have reduced it to the smallest reproducible example, the actual code is something different and quite complex, it checks on various parameters, rather than just comparing getCounter()
values.
How can I kill this mutation or will I have to ignore it.
CodePudding user response:
As performance is not a property of code that is measured by unit testing, code that is present only to optimise performance will result in equivalent mutations that cannot be killed.
As a temporary exercise, you could also have pitest run performance tests, but this would be difficult as they normally give a quantitive result, rather than a boolean pass/fail. It would also be exceedingly slow.
As your optimisation is for when n is low, it would be especially difficult to measure an observable improvement. It may be there is no actual improvement here at all. Unless you performance measurements that justify its presence, the simplest approach would be to reduce the complexity of your code by removing the if statement.
CodePudding user response:
Saying replaced strings.size() == 1 with false but still all the test cases pass.
So, if the test fails, then the PiTest would be happy. I suggest you increase the scope of unit testing to also cover the execution path. Say, if you have a code like this -
String calcMostMatching(String mainStr, List<String> strings) {
String mostMatching = null;
if (strings.size() == 1) {
// if size is 1, return the only present string.
mostMatching = strings.get(0);
} else {
// move the complex code to another method
mostMatching = doSomeComplexAlgo(mainStr, strings);
}
return mostMatching;
}
String doSomeComplexAlgo(String mainStr, List<String> strings) {
// do something complex
return strings.get(0);
}
And inside the Unit testing, we add another verify
method. It verifies that doSomeComplexAlgo
method is not called with a Single String List. Now, if the PiTest testing changes the size
, then the test should fail as doSomeComplexAlgo
will be invoked -
@Test
public void testCalcMostMatchingWithSizeOne() {
SomeTestClass someTestClass = Mockito.spy(new SomeTestClass());
String result = someTestClass.calcMostMatching("", List.of("A"));
// This will fail if the mutation testing changes the List length
Mockito.verify(someTestClass, Mockito.times(0)).doSomeComplexAlgo(Mockito.anyString(), Mockito.anyList());
}