使用JMock获取类<?>类型参数的模拟方法

lui*_*nal 10 java unit-testing mocking jmock junit4

背景:

这是一个JMock + JUnit特定的问题(这是我必须使用的两种技术).是的,我想要做的就是使用PowerMock,但这是一个边缘情况,不保证更换工具.不,对不起,我不是要问这个问题来辩论静态方法的哲学有效性:)

有了这个,我真的要感谢任何人看看这个问题.

题:

我有一些遗留代码,我需要编写一个测试(我们试图围绕继承的代码进行测试,以确保我们在潜在的大规模重构工作中不会破坏任何东西......这是另一个时间的故事.)

目标:

我试图模拟的Foo.bar方法是使用JMock的类imposterizer工具(通过JUnit4Mockery)在下面的类中的方法.

下面的代码代表我正在测试包装的代码:

public class Foo {
     public abstract <T> void bar(
         Class<? extends T> paramClass, T paramT);
Run Code Online (Sandbox Code Playgroud)

我的测试设置旨在允许任何#调用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);
    }
});
Run Code Online (Sandbox Code Playgroud)

然后,我们注入了模拟的Foo,它最终被另一个类调用,我将其称为Driver(*我稍后会回到静态方法调用):

// fooImpl has been replaced/injected with our mock
fooImpl.bar(Snafu.class, someStaticFunctionThatReturnsASnafu()); 
Run Code Online (Sandbox Code Playgroud)

问题:

问题是当在模拟实例上Driver调用bar方法时Foo,我的测试遇到以下异常:

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 .....
Run Code Online (Sandbox Code Playgroud)

显然(或者它看起来对我而言),JMock匹配器将Class实例视为值,无论我们如何尝试匹配它们.或者我错过了什么?

我在许多带有java.lang.Class参数的遗留调用中遇到类似的异常.显然,任何看起来都是X.class值的东西,而不是新的实例.

但其中存在问题,因为另一个参数必须用匹配器来解决,而不仅仅是用实际值来解决.


[*]理想情况下,可以重写静态方法调用

fooImpl.bar(Snafu.class, someStaticFunctionThatReturnsASnafu()); 
Run Code Online (Sandbox Code Playgroud)

更适合嘲弄的东西(非静态方法,另一个对象或用IoC注入的东西).

可能这是我们最终会采用的方式,但目前,相关代码有大量的静态调用.

我想推迟到一个更合适的时刻,而是找到一个通用的JMock解决方案,如果存在的话,这允许我设置像Foo.bar上面那样的模拟函数的必要期望.

Bal*_*der 5

如果我没弄错的话,你在测试用例中做了一切.该文档 JMock的关于使用条款状态

使用参数匹配器的期望必须使用"with"方法来包装每个参数,无论是匹配器函数还是文字值.

这里重要的部分是强调every.你应该只得到IllegalArgumentException你提到的,

java.lang.IllegalArgumentException:并非所有参数都被赋予显式匹配器:要么所有参数都必须由匹配器指定,要么所有参数都必须由值指定,您不能混合使用匹配器和值

如果你将一个带有子句与字面值混合 - 在你的情况下,例如

allowing(mockedFoo).bar(Class.class, with(any(Snafu.class)));
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

此测试用例成功,没有任何运行时异常(使用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)));
Run Code Online (Sandbox Code Playgroud)