这是一个JMock + JUnit特定问题(这些是我必须使用的两种技术)。是的,我想要做的就是使用PowerMock,但这是一个边缘情况,不保证更换工具。不,对不起,我不是要问这个问题来辩论静态方法的哲学有效性:)
有了这个,我真的要感谢任何人看看这个问题。
我有一些我需要编写测试的遗留代码(我们正在尝试围绕继承的代码进行测试,以确保我们在潜在的大规模重构工作中不会破坏任何东西......这是另一个时间的故事。)
我试图模拟的方法是使用JMock的类imposterizer工具(通过JUnit4Mockery)在下面的类中的Foo.bar
方法。)
下面的代码代表我正在测试包装的代码:
public class Foo {
public abstract <T> void bar(
Class<? extends T> paramClass, T paramT);
我的测试设置旨在允许任何#{1}}的调用接收一个Class实例(显然退化为Class ...愚蠢的Java类型擦除“功能”),与任何Snafu实例配对。
这是关键的区别。我没有配对两个Matcher参数,或两个文字参数,但是一个文字(T.class)和任何类型为T的值.JMock不允许这样,所以预期的解决方案将有一个匹配&gt;和匹配者:
bar()
然后,我们注入了模拟的Foo,最终被另一个类调用,我将其称为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);
}
});
(*稍后我会回到静态方法调用):< / p>
Driver
问题是当// fooImpl has been replaced/injected with our mock
fooImpl.bar(Snafu.class, someStaticFunctionThatReturnsASnafu());
在模拟的Driver
实例上调用bar
方法时,我的测试遇到以下异常:
Foo
显然(或者它看起来像我),JMock匹配'看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 .....
个实例作为值,无论我们如何尝试匹配它们。或者我错过了什么?
我在许多带有Class
参数的遗留调用中遇到类似的异常。显然,任何看起来像java.lang.Class
的东西都是值,而不是新的实例。
但其中存在问题,因为其他参数必须用匹配器解决,而不仅仅是实际值。
[*]理想情况下,可以在
中重写静态方法调用X.class
更适合嘲弄的东西(非静态方法,另一个对象或用IoC注入的东西)。
可能这是我们最终会采用的方式,但目前,相关代码会有大量的静态调用。
我想推迟到一个更合适的时刻,而是找到一个通用的JMock解决方案,如果存在,这允许我设置模拟函数的必要期望,如上面的fooImpl.bar(Snafu.class, someStaticFunctionThatReturnsASnafu());
。
答案 0 :(得分:5)
如果我没弄错的话,你在测试用例中做的一切都是正确的。 JMock的documentation关于with子句的状态
使用参数匹配器的期望必须使用“with”方法 包装每个参数,无论是匹配器函数还是文字 值。
这里重要的部分是强调every
。你应该只得到你提到的IllegalArgumentException
,
java.lang.IllegalArgumentException:并非所有参数都已给出 显式匹配器:要么所有参数都必须由匹配器指定 或者所有必须由值指定,你不能混合匹配器和 值
如果要将with子句与字面值混合 - 在您的情况下,例如
allowing(mockedFoo).bar(Class.class, with(any(Snafu.class)));
其中Class.class
是文字值。另请参阅here。
我测试了你的代码,它似乎按预期工作。这是我完整的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)));
答案 1 :(得分:1)
我已经使用了这个,因为它似乎是我能够像我想的那样明确得到的唯一方式:
allowing(mockedFoo).bar(
with(Expectations.<Class<Snafu>>anything()),
with(any(Snafu.class))
);