Missing or extra method parameters

Reproducer

$ pip install nose
$ pip install https://github.com/sixty-north/cosmic-ray/zipball/master

Initially we start with a set of tests which doesn’t validate default values for method parameters. It may validate some other behavior which was considered more important at the time. In this example the test is empty because there is no other behavior present. This is test1.py. Mutation testing will identify the missing tests as shown below

$ cosmic-ray run --test-runner nose --baseline=10 example.json roads.py -- test1.py:
$ cosmic-ray report example.json
job ID 1:Outcome.SURVIVED:roads
command: cosmic-ray worker roads number_replacer 0 nose -- -v test1.py
--- mutation diff ---
--- a/example_09/roads.py
+++ b/example_09/roads.py
@@ -2,7 +2,7 @@

 class UrbanRoad(object):

-    def __init__(self, speedLimit=50, *args, **kwargs):
+    def __init__(self, speedLimit=51, *args, **kwargs):
         self.speedLimit = speedLimit

 class RuralRoad(UrbanRoad):

job ID 2:Outcome.SURVIVED:roads
command: cosmic-ray worker roads number_replacer 1 nose -- -v test1.py
--- mutation diff ---
--- a/example_09/roads.py
+++ b/example_09/roads.py
@@ -7,7 +7,7 @@

 class RuralRoad(UrbanRoad):

-    def __init__(self, speedLimit=90, *args, **kwargs):
+    def __init__(self, speedLimit=91, *args, **kwargs):
         UrbanRoad.__init__(self, *args, **kwargs)

 class BaseMotorway(object):

job ID 3:Outcome.SURVIVED:roads
command: cosmic-ray worker roads number_replacer 2 nose -- -v test1.py
--- mutation diff ---
--- a/example_09/roads.py
+++ b/example_09/roads.py
@@ -13,7 +13,7 @@
 class BaseMotorway(object):

     def __init__(self, *args, **kwargs):
-        self.minSpeed = 50
+        self.minSpeed = 51

 class Motorway(BaseMotorway):


job ID 4:Outcome.SURVIVED:roads
command: cosmic-ray worker roads number_replacer 3 nose -- -v test1.py
--- mutation diff ---
--- a/example_09/roads.py
+++ b/example_09/roads.py
@@ -17,6 +17,6 @@

 class Motorway(BaseMotorway):

-    def __init__(self, speedLimit=130, *args, **kwargs):
+    def __init__(self, speedLimit=131, *args, **kwargs):
         BaseMotorway.__init__(self, speedLimit, *args, **kwargs)


total jobs: 4
complete: 4 (100.00%)
survival rate: 100.00%

Then we proceed to add the missing tests in test2.py. Executing this test stand-alone identifies that something is wrong with the code under test. The Motorway constructor passes along an extra parameter which isn’t consumed in the base class. In the RuralRoad constructor we’ve forgotten to pass the speedLimit parameter to the base constructor.

$ python -m nose test2.py
EF.
======================================================================
ERROR: test_motorway (test2.TestRoadLimits)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./example_09/test2.py", line 15, in test_motorway
    self.assertEqual(road.speedLimit, 130)
AttributeError: 'Motorway' object has no attribute 'speedLimit'

======================================================================
FAIL: test_rural_road (test2.TestRoadLimits)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./example_09/test2.py", line 11, in test_rural_road
    self.assertEqual(road.speedLimit, 90)
AssertionError: 50 != 90

----------------------------------------------------------------------
Ran 3 tests in 0.001s

FAILED (errors=1, failures=1)

Next we proceed to refactor the code under test in roads2.py

--- roads.py    2016-08-26 10:59:58.342135344 +0300
+++ roads2.py   2016-08-26 11:18:30.639663587 +0300
@@ -4,7 +4,7 @@

 class RuralRoad(UrbanRoad):
     def __init__(self, speedLimit=90, *args, **kwargs):
-        UrbanRoad.__init__(self, *args, **kwargs)
+        UrbanRoad.__init__(self, speedLimit, *args, **kwargs)

 class BaseMotorway(object):
     def __init__(self, *args, **kwargs):
@@ -12,4 +12,5 @@

 class Motorway(BaseMotorway):
     def __init__(self, speedLimit=130, *args, **kwargs):
-        BaseMotorway.__init__(self, speedLimit, *args, **kwargs)
+        BaseMotorway.__init__(self, *args, **kwargs)
+        self.speedLimit = speedLimit

In this example the refactored file is called roads2.py and its test is called test3.py.

--- test2.py    2016-08-26 11:15:17.768878361 +0300
+++ test3.py    2016-08-26 11:19:54.241167276 +0300
@@ -1,4 +1,4 @@
-import roads
+import roads2 as roads
 import unittest

 class TestRoadLimits(unittest.TestCase):

Finally we can verify that the test and refactored code work together and all mutants have been killed

$ python -m nose test3.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK
$ cosmic-ray run --test-runner nose --baseline=10 example.json roads2.py -- -v test3.py
$ cosmic-ray report example.json
total jobs: 4
complete: 4 (100.00%)
survival rate: 0.00%

Source code

roads.py
class UrbanRoad(object):
    def __init__(self, speedLimit=50, *args, **kwargs):
        self.speedLimit = speedLimit

class RuralRoad(UrbanRoad):
    def __init__(self, speedLimit=90, *args, **kwargs):
        UrbanRoad.__init__(self, *args, **kwargs)

class BaseMotorway(object):
    def __init__(self, *args, **kwargs):
        self.minSpeed = 50

class Motorway(BaseMotorway):
    def __init__(self, speedLimit=130, *args, **kwargs):
        BaseMotorway.__init__(self, speedLimit, *args, **kwargs)
roads2.py
class UrbanRoad(object):
    def __init__(self, speedLimit=50, *args, **kwargs):
        self.speedLimit = speedLimit

class RuralRoad(UrbanRoad):
    def __init__(self, speedLimit=90, *args, **kwargs):
        UrbanRoad.__init__(self, speedLimit, *args, **kwargs)

class BaseMotorway(object):
    def __init__(self, *args, **kwargs):
        self.minSpeed = 50

class Motorway(BaseMotorway):
    def __init__(self, speedLimit=130, *args, **kwargs):
        BaseMotorway.__init__(self, *args, **kwargs)
        self.speedLimit = speedLimit
test1.py
import unittest

class TestRoadLimits(unittest.TestCase):
    pass

if __name__ == "__main__":
    unittest.main()
test2.py
import roads
import unittest

class TestRoadLimits(unittest.TestCase):
    def test_urban_road(self):
        road = roads.UrbanRoad()
        self.assertEqual(road.speedLimit, 50)

    def test_rural_road(self):
        road = roads.RuralRoad()
        self.assertEqual(road.speedLimit, 90)

    def test_motorway(self):
        road = roads.Motorway()
        self.assertEqual(road.speedLimit, 130)
        self.assertEqual(road.minSpeed, 50)


if __name__ == "__main__":
    unittest.main()
test3.py
import roads2 as roads
import unittest

class TestRoadLimits(unittest.TestCase):
    def test_urban_road(self):
        road = roads.UrbanRoad()
        self.assertEqual(road.speedLimit, 50)

    def test_rural_road(self):
        road = roads.RuralRoad()
        self.assertEqual(road.speedLimit, 90)

    def test_motorway(self):
        road = roads.Motorway()
        self.assertEqual(road.speedLimit, 130)
        self.assertEqual(road.minSpeed, 50)


if __name__ == "__main__":
    unittest.main()