[讀書筆記] The pillars of good tests
1. Writing trustworthy tests
2. Writing maintainable tests
[Maintainability]
Nonmaintainable tests are nightmares because they can ruin project schedules, or you risk losing the tests when the project is put on a more aggressive schedule.
test project 也會越來越多 如果難以維護的話
1. 會破壞Project Schedules
2. 不想再測試
2.1 Testing private or protected methods
Unit tests should be testing the public contract and public functionality of an object應該要針對Public的來測
Solution:
(1) Making Methods Public
If a method is worth testing, it might be worth making it public, static, or at least internal, and defining a public contract against any user of it.
如果你的Method值得測試 那就將他變成Public的吧
(2) Extracting Methods to New Classes
如果Method包含了許多獨立的logic 就將他抽出吧
(3) Making Methods Static
如果Method沒有使用其他變數 可以考慮變成Static的
(4) Making Methods Internal
如果上述方法都失敗 有個最不推薦的方法
[InternalsVisibleTo("TestAssembly")]
Removing the method isn’t a good option because the production code uses the method too. Otherwise, there would be no reason to write the tests in the first place.
移掉Method其實不是一個好選項
2.2 Removing duplication
The “don’t repeat yourself” (DRY) principle
code有重複 就有壞味道
舉例來說:
上面看起來目前很合理
但如果寫了另一個Test
如果Production code 改變了 勢必要回來改這兩個TestMethod
導致更多的maintenance work
假設現在new完LogAnalyzer之後 要先呼叫Initialize()
那麼 我們必須在每個TestMethod裡都要加上呼叫
Initialize()的動作
Solution:
(1) Removing duplication using a helper method
透過GetNewAnalyzer()將 Initialize()封裝在這個Method
如此只要維護這裡即可
(2) Removing duplication using [SetUp]
另一種方法是寫在SetUp Method中
但是缺點就是 不是每個TestMethod都會用到這段邏輯
所以比較不推薦這種作法
2.3 Using setup methods in a maintainable manner
SetUp Method很好用 但是要注意會有以下問題:
(1) Initialize objects that are used by only some of the tests
(2) Having SETUP code that’s lengthy and hard to understand
Refactor it
(3) Setting up fakes in the SETUP method
要考量Fake Objects 是否每個Test Method都會用到
2.4 Enforcing test isolation
原則:
A test should always run in its own little world
如果Test Isolation沒有做好 勢必會相互影響導致錯誤的結果
以下介紹4種沒有隔離的狀況
(1) Constrained test order
測試一定要按照某種順序
(2) Hidden test call
在測試中呼叫其他測試
(3) Shared-state corruption
共享的資源沒有回復
(4) External shared-state corruption
外部共享的資源沒有回復
Constrained test order
i. Flow Testing
通常面對Flow Testing 會產生這種順序問題 所以一個建議的方式不要在UT中測試流程 或這是是採用Integration testing framework
ii. Laziness in cleanup
也有可能是因為懶得清理資源 才導致必須按照某種順序來執行
Hidden test call
i. Flow Testing
同上
ii. Trying to remove duplication
我們可能會為了不想要寫重複的code而在TestMethod中呼叫一個已經寫好的TestMethod 但是千萬不要這麼做
iii. Laziness in separating the tests
別懶得抽出
Shared-state corruption
2.5 Avoiding multiple asserts
Create a separate test for each assert
Use parameterized tests
Wrap the assert call with try-catch
2.6 Avoiding testing multiple aspects of the same object
Create a full object to compare all the properties in one assertOverriding ToString(), let the test output much clearer
2.7 Avoiding overspecification in tests
OVERSPECIFY TESTS that Developers make:
Specifying purely internal behavior
Using STUBS also as MOCKS
Assuming an order or exact match when it’s not needed
xxx