JMockit簡介
在JMockit工具包中,Expectations &Verifications APIs為behavior-based單元測試的建立提供了豐富的支援。這種測試的關注點在於測試單元和其他相關聯的測試單元的互動作用。測試單元包括class,method和constructor。
兩個單元之間的互動通常表現為method或constructor之間的調用。
一個單元測試通常只運用被測試的這一個單元。其背後依賴的一些其他單元並不需要被運用到。因此,單元測試的目的是測試該單元的內部運行邏輯,測試時應該與其依賴的其他單元相孤立。
但是我們在測試時並不是要與所有的依賴單元都孤立開來,通常只對以下的情況做孤立。
1) 已經有(或這將要)自己的單元測試。
2) 由於某些原因導致在測試環境中很難被建立或運行。
對於上述的這些特殊的單元,我們假定這些依賴的行為都是按照預期(expectations)執行的。
Mocked types
在測試單元中被調用的方法或建構函式,以及被依賴到的單元通常都是類比的對象。Mocking提供了一種機制,使得那些被測試的單元可以與他依賴的單元孤立開來。我們聲明某個對象為mocked類型來指定在本測試中該依賴的對象是類比的。能被類比的類型為:interface,class(包括abstract和final類型的),annotation和enum。
在預設情況下,被類比的類型中都有的方法都是被類比的。如果聲明某個class為類比的,則它的所有直到java.lang.Object(但不包含java.lang.Object)的父類都是被類比的。因此繼承的方法也都是自動被類比的。另外在class中都有的建構函式也會被類比。不論是private,static,final還是native的方法/建構函式都能被類比。
當一個方法或建構函式被類比後,在測試期間,他原有的實現代碼就不會被調用。該方法或建構函式的調用就會被重新導向都JMockit中。
以下是一個最基本的例子。
@Test public void doBusinessOperationXyz() { ... new Expectations() { // an "expectation block" Dependency mockInstance; // "Dependency" is our mocked type for this test ... { ... // "mockInstance" is a mocked instance automatically provided for use in the test mockInstance.mockedMethod(...); ... } }; ... }
通常上例中的變數還可以通過@Mocked,@NonStrict,@Injectable這些注釋來聲明是被類比的。
Expectations
一個expectation就是一組在測試中對某個特定類比方法/建構函式的調用。一個expectation可以包含對同一個方法/建構函式的不同調用,但並不需要包含在測試過程中的所有調用。一個調用是否能匹配到某個expectation,不僅僅決定與該法/建構函式的名稱,還取決於運行時的參數,如該方法所屬對象的執行個體,參數值,或被調用的次數等。因此在expectation中可以指定一些匹配調用的約束條件。
我們還可以對expectation中被調用的方法的參數做一些限定,從而匹配到特定條件的方法調用。
如下例中為Dependency#someMethod(int, String)方法的一個expectation,只有滿足該參數值(1,”test”)的方法調用才會匹配到該expectation。
@Test public void doBusinessOperationXyz() { ... new Expectations() { Dependency mockInstance; ... { ... // An expectation for an instance method: mockInstance.someMethod(1, "test"); ... } }; // A call to the unit under test occurs here, leading to mock invocations // that may or may not match specified expectations. }The
record-replay-verify model
所有的測試都可以至少分割為三個階段。
如下所示
@Test public void someTestMethod() { // 1. Preparation: whatever is required before the unit under test can be exercised. ... // 2. The unit under test is exercised, usually by calling a public method. ... // 3. Verification: whatever needs to be checked to make sure the exercised unit // did its job. ... }
首先是準備階段,在該階段中測試時所需要的對象或者資料將被建立或重其他地方擷取來。
然後測試單元被執行。
最後,將運行結果與期待值進行比較。
這個三階段模型也成為:Arrange, Act, Assert文法,(簡稱"AAA")
在使用類比類型的測試中,我們對該三個階段定義如下:
1) Record階段:該階段中調用將被錄製。在測試準備階段,在被測試單元運行之前進行。
2) Replay階段:該階段中在測試單元運行時,類比的調用將可能會被執行。先前被錄製好的對類比方法的調用將會被回放。通常調用的錄製和回放並不一定是一一映射的關係。
3) Verify階段:該階段中可以驗證調用有沒有按照期望運行。在測實驗證階段,調用的方法被執行後運行。
使用JMockit進行的Behavior-based測試可以歸納為以下的模版。
import mockit.*;... other imports ...public class SomeTest{ // Zero or more "mock fields" common to all test methods in the class: @Mocked Collaborator mockCollaborator; @NonStrict AnotherDependency anotherDependency; ... @Test public void testWithRecordAndReplayOnly(mock parameters) { // Preparation code not specific to JMockit, if any. new Expectations() { // an "expectation block" // Zero or more local mock fields. { // One or more invocations to mocked types, causing expectations to be recorded. // Invocations to non-mocked types are also allowed anywhere inside this block. } }; // Unit under test is exercised. // Verification code (JUnit/TestNG asserts), if any. } @Test public void testWithReplayAndVerifyOnly(mock parameters) { // Preparation code not specific to JMockit, if any. // Unit under test is exercised. new Verifications() {{ // a "verification block" // One or more invocations to mocked types, causing expectations to be verified. // Invocations to non-mocked types are also allowed anywhere inside this block. }}; // Additional verification code, if any, either here or before the verification block. } @Test public void testWithBothRecordAndVerify(mock parameters) { // Preparation code not specific to JMockit, if any. new NonStrictExpectations() { // also an expectation block // Zero or more mock fields. { // One or more invocations to mocked types, causing expectations to be recorded. } }; // Unit under test is exercised. new VerificationsInOrder() {{ // also a verification block // One or more invocations to mocked types, causing expectations to be verified // in the specified order. }}; // Additional verification code, if any, either here or before the verification block. }}