On Github switowski / MutationTestingPresentation
1st Developers@CERN Forum
Created by Sebastian Witowski
Richard Lipton, Fault Diagnosis of Computer Programs, 1971
But what if there was an automatic tool for testing you tests ? Well, that's a different story. The same thought had Robert Lipton, when he first came up with the idea of mutation testing in 1971.
Changes similar to small, programming errors.
You start by changing your code in a small way. How small ? Well, very small, for example you can: change the operator or a value in a mathematical equation, replace one variable with another, duplicate a statement, change the boolean operators, etc., etc. So the changes should be similar to small programming errors that each of us do.After that, you run your tests and see what happens. There are two possibilities here. You start getting errors, which means that tests have reached the modified code and: - either they died with some error message, because they didn't expect this modification in the code. For example your function was expecting 2 parameters but suddenly it got only one and that kills your test. This situation is called "weak mutation testing" and it means that your tests are good and in case something changes, you will notice it. - or your tests detects the change properly and informs you about it with the assertion failure - which means that your tests are even better. This situation is called strong mutation testing and it's more powerful - it means that tests are actually catching the possible problems, but it's also more difficult to achieve this.
But what happens if after the mutation, your tests are still working ? Well, it usually means bad things: - either you tests didn't detect the problems that were introduced during the mutation (so your tests are bad), - or the code that was changed was never executed (so you have dead code, which is even worse than bad tests)
def multiply(a, b):
return a * b
Let's write a function that could be used in a simple calculator program.
def multiply(a, b):
return a * b
class CalculatorTest(TestCase):
def test_multiply(self):
self.assertEqual(multiply(2, 2), 4)
And then write the simplest possible test for that.
Now, who can tell me, what is wrong with this test ?
Right, you can replace the multiplication operator with the addition operator (or even with the power operator) and this test will still pass.If you run a mutation testing tool, it will try to replace multiplication operators with various other operators or statements.
If the tool is good, it should detect the mutant that survived and inform you about that.
self.assertEqual(multiply(2, 2), 4)↓
self.assertEqual(multiply(3, 3), 9)So now we know that we need to improve our test. And this is how mutations can help you writing better tests.
index = 0
while True:
do_stuff()
index = index + 1
if index == 10:
break
if index == 10: vs. if index >= 10:
Let me show you an example. Take a look at this simple loop that does some stuff and then increments the index. When we reach 10, we want to break. Now, the mutation tests can replace the equal operator with greater or equal. We can easily tell that even though the syntax is different, both versions of this loop will have exactly the same behavior. However, automatic tools are not as clever as we are. They will see a different syntax that has not been detected by any of the tests and give us a false positive.MutPy (requires Python3)
Before I finish my presentation, I would like to show you a real life mutation testing tool run on the calculator program that I showed you before. I will use the MutPy library that unfortunately works only with Python3.calculator.py
def multiply(a, b):
return a * b
test_calculator.py
from unittest import TestCase
from calculator import multiply
class CalculatorTest(TestCase):
def test_multiply(self):
self.assertEqual(multiply(2, 2), 4)
This is how the code looks like. Just one function inside calculator.py and one test inside test_calculator.py file.
Let's run MutPy on those two files.
self.assertEqual(multiply(2, 2), 4)↓
self.assertEqual(multiply(3, 3), 9)Let's fix our test and run the MutPy again
Happy coding testing !
This presentation is available on github, so you can see the slides on github pages
Thank you, I hope you guys enjoyed the talk and let me know if you have any questions.