jmockit


 

 

行為和狀態的測試:
基於行為的mock是站在目標測試代碼外面的角度的.通常主要模擬行為。
而基於狀態的是站在目標測試代碼內部的。我們可以對傳入的參數進行檢查、匹配,才返回某些結果。Mockup用於state based測試。



一定要理解,單元測試測什么:
單元測試的原則是哪怕你測試的方法中有一行代碼,也是有必要寫測試方法的。所以不要擔心,你測試的業務邏輯最后只剩下邊界值測試。你可以根據一些MOCK的返回值比如空,非空,個數等來測試你的業務邏輯是否正確。如果業務方法依賴於第三方類庫,緩存,消息隊列,DAO層的方法都是可以MOCK的。
以前的思路:Service方法依賴於SDK,針對這些方法的單元測試需要與微博交互返回正確的AccessToken,這樣才能Service做有意義的單元測試。這是典型的依賴於第三方或遠程調用的場景。
正確的思路:新浪的API已經由新浪團隊測試過。我假設它完全正確,只需要模擬它的返回值來測試我的“業務邏輯”在mock指定值下的反應。就象有些Service方法依賴於DAO層,如果DAO層的單元測試充分(比如通過DBUNIT等工具模擬數據),那么在業務層的單元測試中,只需要mock DAO層即可。


Mock的原因:
1)一些依賴單元本身已經(或者將來會有,只是目前還沒有 實現而已)擁有自己的單元測試。
2)由於一些特殊原因,在測試環境中,一些並不是很容易快速的執行的單元(因為它們可能會寫數據庫或者發送郵件等等)。


Dependency是什么?
通過使用mocked聲明,可以在指定的測試代碼對一些特殊的依賴(比如新浪微博的API)進行 mock模擬,也就是說,一個mocked類型,應該是單元測試中的一個依賴類型,這些類型可以是引用、接口、抽象類、具體的類、final 類等等。
@mocked是去修飾Dependency的。



二種指定要mocked的Dependency:

1.傳統的方式:在Expectations里面。

           @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(...);
...
}
};

...
}





2.但大多數情況下,例如變量都是通過一些特定的注解及注解本身的屬性來指定mock,例如 @Mocked, @NonStrict, @Injectable等等。這些注解標記可以在實例的域或者測試方法的參數中使用。
注:不管哪種方式,默認情況下,被mock的類型的所有方法(無論方法或者構造函數的修飾符是否是private,stati,final,native等,這些方法和構造函數都會被mock掉)在測試期間都被 mock實現。如果一個mock類型被聲明為類,那么除了java.lang.Object之外,該類的父類將被遞歸mock。




Expectation:
An expectation is a set of invocations to an specific mocked method/constructor that is relevant for a given test.
為單元測試服務的一組invocations定義。單元測試的replay階段會invoke這些invocations中的某個match invocation。當單元測試中的方法的簽名,運行時的參數(比如invocation的參數值,調用次數)都能match的Expectation中定義的某個invocation時候,該invocation所模擬的行為才會被觸發。



Invocation:
單元測試replay階段invoke一個invocation。如果這個定義在Expectation中的invocation被match到的話。



Record-Replay-Verify模型(jmockit就是遵循這套模型)
任何一個測試至少可以划分三個相互獨立的階段,這些階段將按順序執行,一次執行一個階段

    1.    @Test 
    2.       public void someTestMethod() 
    3.       { 
    4.          // 1. 准備階段:測試執行之前所需要的所有東西都可以編寫在這里 
    5.          ... 
    6.          // 2. 單元測試代碼在這里執行,通常是通過調用public方法來執行 
    7.          ... 
    8.          // 3.  驗證階段:驗證上面所執行的單元測試代碼真正執行了其業務邏輯 
    9.          ... 
    10.       } 

首先,我們有一個准備階段,單元測試代碼所需要的對象和數據都可以在這里創建或者從其他地方加載進來。之后,測試的代碼被執行。最后,測試結果和期望結果進行比較。
(1) Record(記錄)階段:在這里將記錄下哪些調用會被期望執行,這些都是發生在測試的准備階段,而且在真正測試代碼執行調用之前。
(2) Replay(重播)階段:我們感興趣的mock 調用將在這里有機會被執行,就好像真正的單元測試代碼被執行一樣。這些在Record階段記錄下來的 mock方法 /構造函數調用將在這里重播(執行),盡管這些mock調用在record和replay階段通常不是一對一對應的。
(3)Verify(驗證)階段:所有的真實調用應該在這里和期望值進行校驗,這些動作發生在測試驗證階段




JMockit工具來編寫基於行為的測試代碼,通常符合下面的經典模板:

import mockit.*; 
... other imports ...

public class SomeTest
{
// 零個或者更多的mock 屬性, 這些屬性對於整個類的所有測試方法來說是通用的。
@Mocked Collaborator mockCollaborator;
@NonStrict AnotherDependency anotherDependency;
...

@Test
public void testWithRecordAndReplayOnly(mock parameters)
{
// 如果這里需要測試前的准備,可以在這里執行,但對於Jmockit 來說,對此沒特別要求。當然這里也可以為空。

new Expectations() // 一個期望塊.
{
// 零個或者多個局部 mock 屬性域

{
//一個或者多個mock對象(類型)的調用,這些調用會被Expectations記錄(Recorded)下來
//一些沒有被mock的方法、對象類型等同樣可以在這個期望塊里面調用
}
};

// 單元測試代碼真正業務邏輯在此執行

// 如果需要,可以在這里進行驗證代碼編寫,當然可以利用JUnit/TestNG斷言
}

@Test
public void testWithReplayAndVerifyOnly(mock parameters)
{
// 如果這里需要測試前的准備,可以在這里執行,但對於Jmockit 來說,對此沒特別要求。當然這里也可以為空。

// 單元測試代碼真正業務邏輯在此執行

new Verifications() {{ // 一個驗證塊
// 一個或者多個mock對象(類型)的調用,這些調用用於驗證結果是否正確
//一些沒有被mock的方法、對象類型等同樣可以在這個驗證塊里面調用
}};

// 如果需求,這里可以添加其他額外的驗證代碼,
// 當然,這些驗證可以編寫在這里,也可以在Verifications塊之前
}

@Test
public void testWithBothRecordAndVerify(mock parameters)
{
//如果這里需要測試前的准備,可以在這里執行,但對於Jmockit 來說,對此沒特別要求。當然這里也可以為空。

new NonStrictExpectations() { // 同樣是一個期望塊
//零個或者多個局部 mock 屬性域
{
// 一個或者多個mock對象(類型)的調用,這些調用會被Expectations記錄(Recorded)下來
}
};

// 單元測試代碼真正業務邏輯在此執行

new VerificationsInOrder() {{ // 同樣是一個驗證塊
// 一個或者多個mock對象(類型)的調用,這些調用將期望按照特定的順序進行比較。
}};

// 如果需求,這里可以添加其他額外的驗證代碼,
// 當然,這些驗證可以編寫在這里,也可以在Verifications塊之前
}




期望塊與驗證塊的位置及重復:一個測試方法可以包含任意數量(含零個)的期望塊,驗證塊也是一樣。




如何聲明和使用mock類型:


1)字段,期望塊外的字段與期望塊內的局部屬性字段使用@Mocked來聲明Mock類型。

2)參數,方法的參數聲明來引入一個Mock類型。

第一種情況,屬性字段是屬於測試類或者一個mockit.Expectations 子類(一個expectation 期望塊的內部的局部屬性字段)。
第二種情況,參數必須是屬於某個測試方法。    

在所有情況,一個mock 屬性字段或者參數聲明,都可以通過使用@Mocked聲明。
對於方法mock的參數或者 在expectation 期望塊中定義的mock屬性字段來說,該注解是可選的。
可選的原因:
注解@Mocked(或者其他mock的注解類型,例如 @NonStrict)只是對於定義在測試類中的屬性字段域才是必須的,這是為了防止和該測試類的其他不需要mock的字段屬性產生沖突而已。




public interface Dependency // an arbitrary custom interface 
{
String doSomething(boolean b);
}

public final class MultiMocksTest<MultiMock extends Dependency & Runnable> //聲明類型變量MultiMock(它的作用域是整個測試類)
{
@Mocked MultiMock multiMock;

@Test
public void mockFieldWithTwoInterfaces()
{
new NonStrictExpectations() {{
multiMock.doSomething(false); result = "test";
}};

multiMock.run();
assertEquals("test", multiMock.doSomething(false));

new Verifications() {{ multiMock.run(); }};
}

@Test
public <M extends Dependency & Serializable> void mockParameterWithTwoInterfaces(final M mock) //M(它的作用域是 單個測試方法)
{
new Expectations() {{
mock.doSomething(true); result = ""
}};

assertEquals("", mock.doSomething(true));
}
}





對於一個返回值不為void類型的方法,Expectation中如何模擬方法返回值:
1)其返回值可以通過 Expectations#result屬性域
2)Expectations#returns(Object) 方法來進行記錄(Recorded)。
如果這個測試需要獲取一個異常(exception)或者錯誤(error)時,_result屬性域同樣可以使用。很簡單,此時只需要將一個 throwable實例賦值給它就可以了。




* Expectations中模擬方法返回多個可能的示例:
場景:下面的代碼是要測試的業務方法。該業務方法依賴了第三方的DependencyAbc類。通過mock來分別模擬(1)(2)(3)。

public class UnitUnderTest 
{
(1)private final DependencyAbc abc = new DependencyAbc();

public void doSomething()
{
(2) int n = abc.intReturningMethod();

for (int i = 0; i < n; i++) {
String s;

try {
(3) s = abc.stringReturningMethod();
}
catch (SomeCheckedException e) {
// 處理異常
}

// 這里可以處理其他邏輯
}
}
}



@Test
public void doSomethingHandlesSomeCheckedException() throws Exception
{
new Expectations() {
DependencyAbc abc;

{
(1) new DependencyAbc(); //構造方法的模擬。

(2) abc.intReturningMethod(); result = 3;

(3) abc.stringReturningMethod();
returns("str1", "str2");
result = new SomeCheckedException(); //注意,mock異常只能通過result屬性。
}
};

new UnitUnderTest().doSomething();
}



第一個(其實就是 DependencyAbc() 的構造函數調用)實際上會在測試代碼中通過一個無參的構造函數來初始化這些依賴,對於這種調用是不需要任何返回值的,除非在構造函數里面拋出一個異常或者 錯誤(其實構造函數是沒有返回值的,所以對它來說記錄返回值是沒什么意義可說)。第二個期望指定調用intReturningMethod()后將返回值 3。第三個期望就是,調用stringReturningMethod()方法后將按順序返回3個連續的期望值。





默認mock與默認返回值:
1.對於一個返回值不是void的mock方法,無論是否匹配上在Record階段定義的調用期望,都應該對該方法提供默認的返回值。Jmockit總 會根據定義返回值的類型返回一個值:對於整型缺省返回0,boolean類型默認為false,collection或者array會默認為empty對 象,而對於引用類型,則默認為null(包括String類型和JDK原始的包裝類)。
2.mocked的構造函數和返回值為void的 mocked方法,也提供一個"缺省值",只不過就是簡單的return而已,當然沒有拋出異常或者錯誤。




!Expectation invocation與NonStrictExpectations invocation的使用:

Expectation invocation(嚴格期望  or  嚴格invocation) :
在期望塊new Expectations(){...}中,默認所有被記錄下來的期望都是嚴格的。這意味着,這些期望的調用[必須]在重播階段被執行,而且需要按照聲明的期望指定的[執行順序執行]。
而且,[也只允許這些調用被執行]。[任何一個沒有被記錄下來的非期望調用 ] 都會造成測試用例失敗。


NonStrictExpectations invocation(非嚴格期望 or 非嚴格invocation):
在一個非嚴格的期望塊中,所有的被mock的類型的所有調用都可以在重播階段執行,即使不是在期望中聲明的,比如默認mocked的invocation。這在Expectation中完全不可以的。
也就是說,默認情況下,在重播階段是否執行mock類型的調用是不會造成測試用例失敗的。同樣,這種不嚴格的期望是不要求調用的執行順序的。



缺省情況下,一個嚴格的期望會精確匹配重播階段的一個調用。換而言之,這類型的期望是存在一個隱式的調用次數約束1,就好像它后面緊跟着 times=1這個約束。而另一方面,對於一個非嚴格的期望,默認是可以匹配重播階段調用的任意次數的.注意,默認總是可以被顯示指定復蓋。比如指定times/minTimes /maxTimes 屬性字段。



對於一個嚴格的期望,所有在重播階段被期望所匹配的調用,都會隱式被校驗(Verify)通過的。剩余的調用則被認為不符合期望(即造成測試失敗,哪怕這些調用是針對@Mocked形成的默認mocked的方法。),除非, 這個mock的類型被關聯到一個非嚴格的期望上。所以,使用隱式驗證的嚴格期望需要排除verification塊的使用,這個塊是用於調用的顯式校驗的。實際上,在 new Verifications() {...}代碼塊中,只有那些匹配非嚴格期望的調用才被允許使用。






從嚴格到非嚴格:

1.
除了使用針對期望塊的NonStrictExpectations外,還可以使用(@NonStrict注解類屬性+Expectation塊)做到非嚴格效果,注意@NonStrict它是針對類的屬性非嚴格,類的屬性適用於類中的所有測試方法。
這個非嚴格的范圍比非嚴格期望塊的作用范圍大很多。一旦其它的測試方法還想在該屬性的基礎上使用Expectation塊,是不可能的了。如果是這種情況,就需要這樣使用(@Mocked注解類屬性+ NonStrictExpectations塊)。

2.@NonStrict可以避免需要記錄調用構造函數,或任何不感興趣的方法。


3.一旦使用了@NonStrict,Expectation中的invocation就變成了非嚴格的invocation。可以在replay調用或不調用。


總結三種不同嚴格性的方式:1)在一個給定的嚴格期望塊中,如果需要指定某一個期望是非嚴格的,可以調用notStrict()注解方法。2) 對於一個特殊的mock類型/實例,其所有期望都需要是完全非嚴格的,則可以通過注解@NonStrict將其聲明為一個mock屬性字段或者參 數。3)如果在一個期望塊中,需要所有的期望都是非嚴格的,則可以使用NonStrictExpectations類。注意,即使使用NonStrictExpectations,它里面的invocation還是必須在replay階段執行。非嚴格的含義在於replay階段調用非期望的方法是否失敗。

示例:

public class IntroductionTest {


//不管使用下面的二種注解之一,NonStrictExpectations塊中的invocation還是必須在replay的時候調用。只不過比起Expectation塊,replay階段還可以invocate非嚴格的invocation
//getWinportUrlThrowException而不會失敗。
// @NonStrict
@Mocked
private WinportUrlService winportUrlService = null;

@Test
public void testNoExpectations() {
final String memberId = "test2009";
Assert.assertEquals(false, winportUrlService.hasWinport(memberId));
}

@Test
public void testWithExpectations() {
final String memberId = "test2009";


new NonStrictExpectations() {
{
//下面的invocation必須出現在replay階段。
winportUrlService.hasWinport(memberId);
result = false; // 也可以是returns(false);
// 總共可以調用的次數
times = 2;
}
};


// 步驟二、replay
//如果注了下面二句,肯定失敗。並且只invocate一次也不行。
Assert.assertEquals(false, winportUrlService.hasWinport(memberId));
Assert.assertEquals(false, winportUrlService.hasWinport(memberId));
winportUrlService.getWinportUrlThrowException(memberId);

}
}





public class IntroductionTest {

@NonStrict //使用@NonStrict才可以在replay階段invocate getWinportUrlThrowException。
private WinportUrlService winportUrlService = null;

@Test
public void testNoExpectations() {
final String memberId = "test2009";
Assert.assertEquals(false, winportUrlService.hasWinport(memberId));
}

@Test
public void testWithExpectations() {
final String memberId = "test2009";

new Expectations() {
{
winportUrlService.hasWinport(memberId);
result = false; // 也可以是returns(false);
// 總共可以調用的次數
times = 2;
}
};

// 步驟二、replay 在此階段

//Expectation中的invocation必須被調用,而且是2次。
Assert.assertEquals(false, winportUrlService.hasWinport(memberId));
Assert.assertEquals(false, winportUrlService.hasWinport(memberId));
//如果使用@Mocked,那么下面的調用,會報錯。因為隱式驗證會對非期望的invocation驗證失敗。而使用@NonStrict 修改類屬性,則下面的invocate沒有問題。詳情參FullMockTest;如果使用@NonStrict.
winportUrlService.getWinportUrlThrowException(memberId);
}
}





public class IntroductionTest
{

@Mocked
// 使用@Mocked,replay階段invocate getWinportUrlThrowException會報失敗。
private WinportUrlService winportUrlService = null;

@Test
public void testNoExpectations()
{
final String memberId = "test2009";
Assert.assertEquals(false, winportUrlService.hasWinport(memberId));
}

@Test
public void testWithExpectations()
{
final String memberId = "test2009";

new Expectations()
{
{
// 期望被mock的調用,以及被調用時返回的結果
winportUrlService.hasWinport(memberId);
result = false; // 也可以是returns(false);
// 總共可以調用的次數
times = 2;
}
};

// 步驟二、replay 在此階段

// Expectation中的invocation必須被調用,而且是2次。
Assert.assertEquals(false, winportUrlService.hasWinport(memberId));
Assert.assertEquals(false, winportUrlService.hasWinport(memberId));
// 如果使用@Mocked,那么下面的調用,會報錯。因為隱式驗證會對非期望的invocation驗證失敗。詳情參FullMockTest;如果使用@NonStrict.
winportUrlService.getWinportUrlThrowException(memberId);
}
}







關於Verify:

校驗用來驗證replay階段是否按照了一定的順序執行或者驗證replay階段的invocate是否匹配到了期望里面的invocation,即調用次數是否匹配。感覺主要針對replay階段的正確性進行驗證。
Explicit verification使用場合:
嚴格期望是隱式校驗的,所以,不需要在一個顯式的校驗塊重復校驗。非嚴格期望通常在一個校驗結構塊中通過顯式的方式來校驗mock類型的調用。就如不久將看到的一樣,一個被錄制下來的非嚴格期望仍然可以通過隱式方式來校驗,而不需要在校驗塊中編碼調用校驗。




Partial Mocked總結:

背景:使用@Mocked, @NonStrict, @Injectable修飾的對象,所有默認方法都是mocked狀態。而如果不想對所有方法mocked,那么可以使用Partial Mock來解決。這與嚴格非嚴格沒有什么關系。主要是針對注解修飾屬性形成方法默認為mocked狀態這種情況。有些時候,針對第三方API,某個類的部分方法並不希望mocked,而是保持原來的調用。這樣的場景是比較特殊的。平常第三方的類或接口都是全部mocked。


This is appropriate for most tests, but in some situations we might need to select only certain methods or constructors to be mocked; or we might prefer to exclude certain methods/constructors from being mocked. Methods/constructors not mocked in an otherwise mocked type will execute normally when called.



二種方式實現:
1.static partial mocking
1) 特征:使用@Mocked(正則字符串)

2)示例:

public class MyTestClass
{
@Mocked("nanoTime") final System system = null;
@Mocked("print") final JComponent component = new JButton();

@Test
//”()“代表構造方法。
public void staticPartialMocking(@Mocked(methods = "()", inverse = true) final Graphics graphics)
{
...

new Expectations() {
@Mocked({"(int)", "doInternal()", "[gs]etValue", "complexOperation(Object)"})
Collaborator mock;

{
graphics.setClip(0, 0, 80, 60);
graphics.translate(-15, -12);
component.print(graphics);

mock.getValue();
}
};

...
}
}




3)關於inverse=true,表示正則匹配到的方法不mocked。默認是false,表示正則匹配到的方法mocked。

For example, while @Mocked("nanoTime") or @Mocked(methods = "nanoTime", inverse = false) select only the System#nanoTime() method to be mocked, the inverse specification @Mocked(methods = "nanoTime", inverse = true) select it as the only method not mocked in the java.lang.System class.


關於正則如何過濾不需要mocked的方法:從當前類一直往上直到Object。整個樹里面的方法都需要跟正則匹配。

Finally, notice that when the mocked type is a class, a mocking filter will match methods/constructors defined anywhere in the class hierarchy, from the mocked class up to (but not including) Object


2.Dynamic partial mocking

1)動態部分mocked, 避免了靜態使用正則式的工作量及使用字符串不好重構的弊端。

2)特征:不在使用注解的時候顯示指定。而是在RRV的第二個階段,Replay的時候,通過與期望的invocation匹配決定。如果匹配到了,那么就是mocked的方法。如果匹配不到就按照非mocked的方法處理。

An alternative that avoids both problems is to let JMockit figure out whether to execute the real implementation of methods/constructors during the replay phase,based on which invocations were recorded and which were not.

if it matches a recorded expectation, it gets mocked; otherwise, the real implementation gets executed.

3)期望(包括嚴格期望塊與非嚴格期望塊)參數有二種:類和實例

4)示例:

public final class DynamicPartialMockingTest
{
static class Collaborator
{
private final int value;

Collaborator() { value = -1; }
Collaborator(int value) { this.value = value; }

int getValue() { return value; }
final boolean simpleOperation(int a, String b, Date c) { return true; }
static void doSomething(boolean b, String s) { throw new IllegalStateException(); }
}

@Test
public void dynamicallyMockAClass()
{
new Expectations(Collaborator.class) {{
new Collaborator().getValue(); result = 123;
}};

// Mocked:
Collaborator collaborator = new Collaborator();
assertEquals(123, collaborator.getValue());

// Not mocked:
assertTrue(collaborator.simpleOperation(1, "b", null));
assertEquals(45, new Collaborator(45).value);
}

@Test
public void dynamicallyMockAnInstance()
{
final Collaborator collaborator = new Collaborator(2);

new NonStrictExpectations(collaborator) {{
collaborator.simpleOperation(1, "", null); result = false;
Collaborator.doSomething(anyBoolean, "test");
}};

// Mocked:
assertFalse(collaborator.simpleOperation(1, "", null));
Collaborator.doSomething(true, null);

// Not mocked:
assertEquals(2, collaborator.getValue());
assertEquals(45, new Collaborator(45).value);
assertEquals(-1, new Collaborator().value);
}
}





注意
1)實例做為參數,當前類中的方法可以mocking。如果類做為參數,直到Object中的所有方法都可以mocking。注意,不是mocked狀態。mocking表示的是一種可以mocking的范圍。最終mocked方法是匹配到invocation才是mocked狀態。而實例做為參數的范圍表示僅在當前類中匹配。類做為參數的范圍表示可以在父類中匹配。


2) 與FullMock的一個區別是沒有用注解去修飾類屬性。
Notice that in these two example tests there is no mock field or mock parameter. Dynamic mocking effectively provides yet another way to specify mocked types. It also lets us turn objects stored in local variables into mocked instances. Such objects can be created with any amount of state in internal instance fields; they will keep that state when mocked.


3) 動態不是讓開發人員象靜態那樣通過正則指定,而是告訴jmockit怎么去區分哪些是mock哪些是real.注意,mocked 和 mocking
An alternative that avoids both problems is to let JMockit figure out whether to execute the real implementation of methods/constructors during the replay phase,




一個示例:
Let's say I have class MyClass with methods x(), y() and z(). Let's say x() calls y(), and y() calls z().
So everytime I test x() both y() and z() are called. In case of mocking the dependencies of MyClass I will have to mock the dependencies behavior inside x(), y() and z().
So if my tests for method x() are testXWhen1(), testXWhen2() and testXWhen3() I will have to repeat the expectations for my dependencies in each of the test methods. In the end, I have some code with the expectations for what happens inside y() and z() repeated for my three test methods. Any solution to avoid this?
One of my ideas was to try to test the actual x() method, but mocking y() and z(). In that case my instance of MyClass should be partly a mock and partly the real MyClass. Is it possible?
Another solution was to be strict about expectations in x(), but not about what happens in y() and z()... I think I can do that with @NonStrict instead of @Mocked, but it's not my favorite solution.(提問者明顯混淆了Partial與非嚴格,這是所有初學者會范的錯誤)
->

If you want to test method x() then you should mock method y().In That case there's no need to mock z() too 'cause you'll never reach call of z() inside the y() (y is mocked).Test your x, y and z methods in different tests.Use PowerMock.It has createPartialMock method.

->

You can use JMockit's dynamic partial mocking feature, by passing the class or object to be partially mocked in the Expectations/NonStrictExpectations constructor.(由此可見,Partial/Full與 嚴格/非嚴格完全不相關,是獨立的二個使用場景。)
In general, though, it's best to avoid the use of partial mocking, as it often indicates a lack of cohesion in the code under test, and leads to tests that are harder to understand. (部分Partial是不推薦使用的。因為不好理解。)
For cases where you have a bunch of the same expectations being needed in several tests, there's always the option of creating reusable expectation blocks. You can encapsulate a number of expectations in a named "XyzExpectations" subclass, optionally with parameterized constructors, and instantiate it in any number of tests (the "expectations" subclass actually instantiated must be final). The same can be done with verification blocks.(針對重復Expectation的解決辦法。不過,通常x()只需要被測試一次就OK了。沒必須出現在三個測試方法里。所以,這樣的場景也很少見。)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM