[英]Mocking Method that Takes a Class<?> Type Argument with JMock
這是一個針對JMock + JUnit的問題(這是我必須使用的兩種技術)。 是的,我想做的事可以通過PowerMock來完成,但這是一個極端的情況,不能保證更換工具。 不,對不起,我不是在問這個問題來討論靜態方法的哲學有效性:)
有了這個解決方案,我真的要感謝任何關注此問題的人。
我有一段遺留代碼,我需要為其編寫測試(我們正在嘗試對繼承的代碼進行測試,以確保在潛在的大量重構工作中不會破壞任何內容……這是另外一次了。)
我要模擬的方法是使用JMock的類冒名者工具(通過JUnit4Mockery )在下面的類中的Foo.bar
方法。
下面的代碼代表了我正在測試包裝的代碼:
public class Foo {
public abstract <T> void bar(
Class<? extends T> paramClass, T paramT);
我的測試設置旨在允許對bar()
任何調用都接收一個Class實例(顯然會退化為Class ...愚蠢的Java類型擦除“功能”),並與Snafu的任何實例配對。
這是這里的關鍵區別。 我沒有將兩個Matcher參數或兩個文字參數配對,而是將一個文字(T.class)和任何類型T的值配對。JMock不允許這樣做,因此預期的解決方案是使用Matcher>和Matcher:
Foo mock = context.mock(Foo.class);
context.checking(new Expectations() {
// keep warnings close to the culprit code when possible
@SuppressWarnings("unchecked")
public void allow(final Foo mockedFoo) {
allowing(mockedFoo).bar(
with(any(Snafu.class.getClass())), // Matcher that *should* resolve to Class<?>
with(any(Snafu.class))); // matcher to anything of type Snafu.class
}
{
allow(mockedFoo);
}
});
然后,我們注入模擬的Foo,它最終會被另一個類這樣調用,我將其稱為Driver
(*稍后將返回到靜態方法調用):
// fooImpl has been replaced/injected with our mock
fooImpl.bar(Snafu.class, someStaticFunctionThatReturnsASnafu());
問題是,當Driver
在模擬的Foo
實例上調用bar
方法時,我的測試遇到以下異常:
java.lang.IllegalArgumentException: not all parameters were given explicit matchers: either all parameters must be specified by matchers or all must be specified by values, *you cannot mix matchers and values*
at org.jmock.internal.InvocationExpectationBuilder.checkParameterMatcherCount(InvocationExpectationBuilder.java:98)
at org.jmock.internal.InvocationExpectationBuilder.createExpectationFrom(InvocationExpectationBuilder.java:91)
at org.jmock.internal.InvocationToExpectationTranslator.invoke(InvocationToExpectationTranslator.java:19)
at org.jmock.internal.FakeObjectMethods.invoke(FakeObjectMethods.java:38)
at org.jmock.lib.legacy.ClassImposteriser$4.invoke(ClassImposteriser.java:129)
at .....
顯然(在我看來,如此),無論我們嘗試如何匹配,JMock匹配器都將Class
實例視為值。 還是我錯過了什么?
在許多使用java.lang.Class
參數的舊式調用中,我遇到了類似的異常。 顯然,任何看起來像X.class
東西都是值,而不是新實例。
但是這里存在問題,因為另一個參數必須使用匹配器而不是實際值來解決。
[*]理想情況下,可以重寫靜態方法調用
fooImpl.bar(Snafu.class, someStaticFunctionThatReturnsASnafu());
具有更適合模擬的東西(非靜態方法,另一個對象或注入了IoC的東西)。
也許這就是我們最終將要采用的方式,但是就目前而言,所討論的代碼具有大量的靜態調用。
我想將其推遲到更合適的時刻,而不是找到一個通用的JMock解決方案(如果存在),該解決方案使我能夠設置像上面的Foo.bar
這樣的Foo.bar
函數的必要期望。
如果我沒記錯的話,那么您在測試案例中所做的一切都是正確的。 JMock有關with子句的文檔說明
使用參數匹配器的期望值必須使用“ with”方法來包裝每個參數,無論是匹配器函數還是文字值。
這里的重要部分是對every
方面的強調。 您應該只獲得您提到的IllegalArgumentException
,
java.lang.IllegalArgumentException:並非所有參數都被賦予顯式匹配器:要么必須由匹配器指定所有參數,要么必須由值指定所有參數,則不能混合匹配器和值
如果您將with子句與文字值混合-例如
allowing(mockedFoo).bar(Class.class, with(any(Snafu.class)));
其中Class.class
是文字值。 另請參閱此處 。
我測試了您的代碼,它似乎按預期工作。 這是我完整的JUnit TestCase
:
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JUnit4Mockery;
import junit.framework.TestCase;
public class FooTest extends TestCase{
Mockery context = new JUnit4Mockery();
public interface Foo {
public abstract <T> void bar(Class<? extends T> paramClass, T paramT);
}
public static class Snafu {}
public void testFoo() {
final Foo mock = context.mock(Foo.class);
context.checking(new Expectations() {
// keep warnings close to the culprit code when possible
@SuppressWarnings("unchecked")
public void allow(final Foo mockedFoo) {
allowing(mockedFoo).bar(
with(any(Class.class)), // Matcher that *should* resolve to Class<?>
with(any(Snafu.class))); // matcher to anything of type Snafu.class
}
{
allow(mock);
}
});
// test bar method (two invocations)
mock.bar(Snafu.class, someStaticFunctionThatReturnsASnafu());
mock.bar(Snafu.class, someStaticFunctionThatReturnsASnafu());
}
public static Snafu someStaticFunctionThatReturnsASnafu() {
return new Snafu();
}
}
此測試用例成功完成,沒有任何運行時異常(已使用JUnit 4和JMock 2.6.0進行了測試)。 我使用with(any(Class.class))
而不是with(any(Snafu.class.getClass()))
來提高可讀性,但這並不重要。
如果將其更改為,我只會得到提到的IllegalArgumentException
。
allowing(mockedFoo).bar(Class.class, with(any(Snafu.class)));
我之所以使用它,是因為這似乎是我可以明確表達的唯一方法:
allowing(mockedFoo).bar(
with(Expectations.<Class<Snafu>>anything()),
with(any(Snafu.class))
);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.