[讀書筆記] Interaction testing using mock objects
[前言]
在前一章Stubs 我們使用了Stub 來打破了Dependency但是現實上 卻有很多的function是
沒有回傳值
沒有存狀態
針對這些Function該如何測試呢?
答案是 Mock Objects
1.1 Value-based vs. state-based vs. interaction testing
Value-based testing
Checks the value returned from a function.
State-based testing
Checks the noticeable behavior changes in the system under test, after changing its state.
Interaction testing
Tests how an object sends input to or receives input from other objects—how that object interacts with other objects.
舉個例子:
假設有一個灌溉系統
Value-based testing
Check 水龍頭是否可以打開
State-based testing
Check 土壤濕度
Interaction testing
在水龍頭端裝置一台監控設備 紀錄灌溉的起始結束時間以及水量 只需檢查監控設備的次數及水量是否正確
Sometimes state-based testing is the best way to go because interaction testing is too difficult to pull off.
State-based testing 有時是最好的測試方法 因為Interaction testing實在太難達到了
[Definition]
Mock object
A mock object is a fake object in the system that decides whether the unit test has passed or failed. It does so by verifying whether the object under test interacted as expected with the fake object. There’s usually no more than one mock per test.
1.2 The difference between mocks and stubs
[Definition]
Fake
A fake is a generic term that can be used to describe either a stub or a mock object, because they both look like the real object. Whether a fake is a stub or a mock depends on how it’s used in the current test. If it’s used to check an interaction (asserted against), it’s a mock object. Otherwise, it’s a stub.
Mocks 跟 Stubs最主要的區別如下圖:
Stub:
呼叫Assert的是 TestClass
code的例子:
Mock
用Mock Object來確認Test Pass or not
code的例子:
1.3: A simple handwritten mock example
假設新增一個需求:
當LogAnalyzer收到一個長度短於8的filename就呼叫外部WebService去記錄LogError
測試上 我們用MockWebService來替換掉外部WebService
此時我們要測的是 WebService是否確實有記錄到LogError
Why don't we write the tests directly inside the mock object code?
Reason:
1. We’d like to be able to reuse the mock object in other test cases, with other asserts on the message.
要Reuse Mock Object
2. If the assert were put inside the mock object, whoever reads the test would have no idea what we’re asserting.
寫在Mock裡 其他人會不知道在assert什麼
1.4 Using a mock and a stub together
假設再次新增一個需求:
如果WebService發生了Exception 系統要寄email給IT管理員
此時LogAnalyzer有兩個外部Dependence
如何測試當Exception發生時 有確實發送了Email?
Questions:
- Why are we doing several asserts in a single test? How easy would it be to separate this test into three different tests with one assert each?Could the three asserts be combined into a single logical test?
- It can be quite tedious to create manual mocks and stubs for each test or test class. How can we overcome that?
- Couldn’t we have used the MockService from listing 4.1 as a stub?
1.5 One mock per test
Principle:
- In a test where you test only one thing
- Only one mock object in a test
- Other fake objects are all stubs
1.6 Stub chains: stubs that produce mocks or other stubs
當一層接一層時 怎麼測試?
ex.
你可能要寫 factory 跟 service兩個stubs
或者 Confuguration 跟 DBConfiguration兩個stubs
假如你想要替換掉 connstring
Solution:
1. 用Extract and Override
2. 用Adapt Parameter Pattern
1.7 The problems with handwritten mocks and stubs
1. It takes time to write the mocks and stubs.
2. It’s difficult to write stubs and mocks for classes and interfaces that have many methods, properties, and events.
3. To save state for multiple calls of a mock method, you need to write a lot of boilerplate code to save the data.
<boilerplate : 照本宣科的規範;(新聞業)一成不變的陳腐文字>
為了在多次呼叫Mock Methods之後儲存狀態 需要寫很多code
4. If you want to verify all parameters on a method call, you need to write multiple asserts. If the first assert fails, the others will never run, because a failed assert throws an exception.
如果需要check 一個method的所有參數 需要多個 assert
但是只要一個assert Fail 就會中斷這次測試
5. It’s hard to reuse mock and stub code for other tests.
0 意見:
張貼留言