重構 if X is not None

任何型態的物件都可以跟 None 來比較,不過這會讓突變存活下來。None 是一個特別的值,不過對於大部份常見的案例中這都等價於 0 或空值。

範例:

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

example1.py 我們使用 None 當作 password 的預設參數並且正確的偵測出 3 個測試案例 - 當 password 是字串時,當參數傳入空字串而我們使用 None 比較時,會有一個突變存活下來。

$ cosmic-ray run --test-runner nose --baseline=10 example.json example1.py -- test1.py:
$ cosmic-ray report example.json

job ID 5:Outcome.SURVIVED:example1
command: cosmic-ray worker example1 replace_IsNot_with_NotEq 0 nose -- -v test1.py
--- mutation diff ---
--- a/example_11/example1.py
+++ b/example_11/example1.py
@@ -1,7 +1,7 @@


 def reverse_password(password=None):
-    if (password is not None):
+    if (password != None):
         return password[::(- 1)]
     else:
         return ''

total jobs: 11
complete: 11 (100.00%)
survival rate: 9.09%

如果我們移除 None 而使用空字串作為預設值,在 example2.py 中可以減少一行程式碼並且讓突變都被殺死。同時突變數量也有顯著的減少。

$ cosmic-ray run --test-runner nose --baseline=10 example.json example2.py -- -v test2.py
$ cosmic-ray report example.json
job ID 1:Outcome.KILLED:example2
command: cosmic-ray worker example2 number_replacer 0 nose -- -v test2.py

job ID 2:Outcome.KILLED:example2
command: cosmic-ray worker example2 arithmetic_operator_deletion 0 nose -- -v test2.py

total jobs: 2
complete: 2 (100.00%)
survival rate: 0.00%

程式碼

example1.py
def reverse_password(password = None):
    if password is not None:
        return password[::-1]
    else:
        return ''
test1.py
import example1 as example
import unittest

class TestReversePassword(unittest.TestCase):
    def test_non_empty_password(self):
        revpass = example.reverse_password('secret')
        self.assertEqual(revpass, 'terces')

    def test_empty_password(self):
        revpass = example.reverse_password('')
        self.assertEqual(revpass, '')

    def test_no_password_provided(self):
        revpass = example.reverse_password()
        self.assertEqual(revpass, '')

if __name__ == "__main__":
    unittest.main()
example2.py
def reverse_password(password = ''):
    return password[::-1]
test2.py
import example2 as example
import unittest

class TestReversePassword(unittest.TestCase):
    def test_non_empty_password(self):
        revpass = example.reverse_password('secret')
        self.assertEqual(revpass, 'terces')

    def test_empty_password(self):
        revpass = example.reverse_password('')
        self.assertEqual(revpass, '')

    def test_no_password_provided(self):
        revpass = example.reverse_password()
        self.assertEqual(revpass, '')

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