單元測試原則 單元測試 (unit test) 表示針對一個片段的程式碼給定某個測項 input 來執行此程式片段, 並輸出 output 來比較是否符合預期的結果。
Given-When-Then 原則 此為基本寫一個單元測試的結構:
Given: 表示要測的測向 input When: 被測的程式碼執行 context Then: 預期出來的結果
1 2 3 4 5 6 7 8 9 10 11 12 13 def add (a, b ): return a + b def test_add (): a, b = 2 , 3 result = add(a, b) assert result is 5
程式模組化 程式再撰寫時,必須要把本身的邏輯給拆分(ex: function, module),才比較好導入測試。因此,要做 unit test 程式碼本身必須有一定的模組化。
測試隔離原則 程式邏輯多少會牽扯到外部的相依性呼叫 ex: (DB query, call API) 必須要把程式有牽扯到外部的相依性的邏輯盡可能的去 decouple,如此才比較好導入單元測試。真的無法避免在考慮使用 mock 或 stub 的方式來替代外部環境依賴。
一個功能,一個 Assert 寫單元測試也務必遵守單一職責原則,就是一個測試儘可能專注於一個 test case,如此測試的定義才會清楚,才不會在執行時模糊不清。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def test_module1 (): assert module1.foo1() is "foo1" assert module1.foo2() is "foo2" assert module1.foo3() is "foo3" def test_module1_foo1 (): assert module1.foo1() is "foo1" def test_module1_foo2 (): assert module1.foo2() is "foo2" def test_module1_foo3 (): assert module1.foo3() is "foo3"
測試獨立原則 單元測試中每個 test case 彼此間都是完全獨立的且不可相依,彼此測試間都有獨立的環境啟動以及結束,此即為 setup
以及 tear down
操作。假設有三個測試啟動以及結束都會個別觸發 setup
以及 tear down
三次。以下以 pytest 為範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class TestClass : def setup_class (self ): print ("setup_class called once for the class" ) def teardown_class (self ): print ("teardown_class called once for the class" ) def setup_method (self ): print ("setup_method called for every method" ) def teardown_method (self ): print ("teardown_method called for every method" ) def test_one (self ): print ("one" ) assert True print ("one after" ) def test_two (self ): print ("two" ) assert False print ("two after" ) def test_three (self ): print ("three" ) assert True print ("three after" )
執行此測試
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ pytest -s test_class.py setup_class called once for the class setup_method called for every method one one after teardown_method called for every method setup_method called for every method two teardown_method called for every method setup_method called for every method three three after teardown_method called for every method teardown_class called once for the class
reference:
https://martinfowler.com/bliki/UnitTest.html
https://martinfowler.com/bliki/GivenWhenThen.html
https://code-maven.com/slides/python/pytest-class