簡體   English   中英

類的模擬方法 <?> 用JMock輸入參數

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM