2014-11-06

[讀書筆記] Interaction testing using mock objects

[讀書筆記] 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 意見: