測試 if == string 序列

有時候程式中會有這樣的模式

if X == "string_1":
    pass
elif X == "string_2":
    pass
elif X == "string_3":
    pass

X 與許多合法的字串去做比較。當我們忘記測試合法字串外的值的時候,突變測試會產生存活的突變。

備註

順序並不是這麼的重要

Reproducer

$ pip install nose
$ pip install https://github.com/sixty-north/cosmic-ray/zipball/master
$ celery -A cosmic_ray.tasks.worker worker

$ cosmic-ray run --test-runner nose --baseline=10 example.json selinux_str.py -- tests_str.py:Test_mode_from_str
$ cosmic-ray report example.json

job ID 4:Outcome.SURVIVED:selinux_str
command: cosmic-ray worker selinux_str mutate_comparison_operator 2 nose -- tests_str.py:Test_mode_from_str
--- mutation diff ---
--- a/example_08/selinux_str.py
+++ b/example_08/selinux_str.py
@@ -3,7 +3,7 @@

 def mode_from_str(str_mode):
     retval = None
-    if (str_mode == 'disabled'):
+    if (str_mode <= 'disabled'):
         retval = modes.SELINUX_DISABLED
     elif (str_mode == 'enforcing'):
         retval = modes.SELINUX_ENFORCING

job ID 8:Outcome.SURVIVED:selinux_str
command: cosmic-ray worker selinux_str mutate_comparison_operator 6 nose -- tests_str.py:Test_mode_from_str
--- mutation diff ---
--- a/example_08/selinux_str.py
+++ b/example_08/selinux_str.py
@@ -3,7 +3,7 @@

 def mode_from_str(str_mode):
     retval = None
-    if (str_mode == 'disabled'):
+    if (str_mode in 'disabled'):
         retval = modes.SELINUX_DISABLED
     elif (str_mode == 'enforcing'):
         retval = modes.SELINUX_ENFORCING

job ID 12:Outcome.SURVIVED:selinux_str
command: cosmic-ray worker selinux_str mutate_comparison_operator 10 nose -- tests_str.py:Test_mode_from_str
--- mutation diff ---
--- a/example_08/selinux_str.py
+++ b/example_08/selinux_str.py
@@ -5,7 +5,7 @@
     retval = None
     if (str_mode == 'disabled'):
         retval = modes.SELINUX_DISABLED
-    elif (str_mode == 'enforcing'):
+    elif (str_mode <= 'enforcing'):
         retval = modes.SELINUX_ENFORCING
     elif (str_mode == 'permissive'):
         retval = modes.SELINUX_PERMISSIVE

job ID 16:Outcome.SURVIVED:selinux_str
command: cosmic-ray worker selinux_str mutate_comparison_operator 14 nose -- tests_str.py:Test_mode_from_str
--- mutation diff ---
--- a/example_08/selinux_str.py
+++ b/example_08/selinux_str.py
@@ -5,7 +5,7 @@
     retval = None
     if (str_mode == 'disabled'):
         retval = modes.SELINUX_DISABLED
-    elif (str_mode == 'enforcing'):
+    elif (str_mode in 'enforcing'):
         retval = modes.SELINUX_ENFORCING
     elif (str_mode == 'permissive'):
         retval = modes.SELINUX_PERMISSIVE

job ID 20:Outcome.SURVIVED:selinux_str
command: cosmic-ray worker selinux_str mutate_comparison_operator 18 nose -- tests_str.py:Test_mode_from_str
--- mutation diff ---
--- a/example_08/selinux_str.py
+++ b/example_08/selinux_str.py
@@ -7,7 +7,7 @@
         retval = modes.SELINUX_DISABLED
     elif (str_mode == 'enforcing'):
         retval = modes.SELINUX_ENFORCING
-    elif (str_mode == 'permissive'):
+    elif (str_mode <= 'permissive'):
         retval = modes.SELINUX_PERMISSIVE
     return retval


job ID 22:Outcome.SURVIVED:selinux_str
command: cosmic-ray worker selinux_str mutate_comparison_operator 20 nose -- tests_str.py:Test_mode_from_str
--- mutation diff ---
--- a/example_08/selinux_str.py
+++ b/example_08/selinux_str.py
@@ -7,7 +7,7 @@
         retval = modes.SELINUX_DISABLED
     elif (str_mode == 'enforcing'):
         retval = modes.SELINUX_ENFORCING
-    elif (str_mode == 'permissive'):
+    elif (str_mode >= 'permissive'):
         retval = modes.SELINUX_PERMISSIVE
     return retval


job ID 24:Outcome.SURVIVED:selinux_str
command: cosmic-ray worker selinux_str mutate_comparison_operator 22 nose -- tests_str.py:Test_mode_from_str
--- mutation diff ---
--- a/example_08/selinux_str.py
+++ b/example_08/selinux_str.py
@@ -7,7 +7,7 @@
         retval = modes.SELINUX_DISABLED
     elif (str_mode == 'enforcing'):
         retval = modes.SELINUX_ENFORCING
-    elif (str_mode == 'permissive'):
+    elif (str_mode in 'permissive'):
         retval = modes.SELINUX_PERMISSIVE
     return retval


total jobs: 25
complete: 25 (100.00%)
survival rate: 28.00%

殺死突變

要殺死突變我們需要測試合法數值外的值:

$ cosmic-ray run --test-runner nose --baseline=10 example.json selinux_str.py -- tests_str.py:TestCompletely
$ cosmic-ray report example.json

剩餘突變

當在測試字串比較時 ==/in 兩個突變是相等的且不能被殺死

-    elif (str_mode == 'permissive'):
+    elif (str_mode in 'permissive'):

程式碼

modes.py
SELINUX_DISABLED = 0
SELINUX_ENFORCING = 1
SELINUX_PERMISSIVE = 2
selinux_str.py
import modes

def mode_from_str(str_mode):
    retval = None

    if str_mode == "disabled":
        retval = modes.SELINUX_DISABLED
    elif str_mode == "enforcing":
        retval = modes.SELINUX_ENFORCING
    elif str_mode == "permissive":
        retval = modes.SELINUX_PERMISSIVE

    return retval
tests_str.py
import modes
import selinux_str
import unittest

class Test_mode_from_str(unittest.TestCase):
    def test_with_disabled(self):
        m = selinux_str.mode_from_str("disabled")
        self.assertEqual(m, modes.SELINUX_DISABLED)

    def test_with_enforcing(self):
        m = selinux_str.mode_from_str("enforcing")
        self.assertEqual(m, modes.SELINUX_ENFORCING)

    def test_with_permissive(self):
        m = selinux_str.mode_from_str("permissive")
        self.assertEqual(m, modes.SELINUX_PERMISSIVE)


class TestCompletely(Test_mode_from_str):
    def test_with_values_outside_set(self):
        for mode in ['aaaaa', 'zzzzz']:
            m = selinux_str.mode_from_str(mode)
            self.assertEqual(m, None)

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