为什么 JMockIt 在此代码段中模拟 getter X.getE() for class X?
Why does JMockIt mock the getter X.getE() for class X in this snippet?
注意:问题末尾有完整的代码片段
我有一个 JMockIt 测试 class Service
有两个依赖项(片段中的 Factory
和 Consumer
)。该服务在内部向 Factory
依赖项请求 class X
的 object,然后将 object 传递给此方法中的 Consumer
依赖项:
public void interact(int i, E e) {
X v = f.create(i, e);
c.consume(v);
}
在我的测试中,我想使用 Service.interact()
方法和 postulate 严格期望消费者应收到满足特定属性的参数值。现在,由于 X
类型的参数是一个没有实现 equals() 的 POJO,我想使用它提供的 getter 方法来执行我的检查; X
看起来像这样(问题末尾有完整的片段):
public static class X {
private final int i;
public final E e;
// ... see below for full snippet
public int getI() { return i; }
public E getE() { return e; } // E is an enum
所以我所做的是尝试检查消费者是否收到了 X.getI() == 2
和 X.getE() == E.B
的值,我使用 with(Delegate<X>)
和 withArgThat(Matcher<X>)
来执行此操作在我的期望中:
new StrictExpectations() {{
consumer.consume(withArgThat(new BaseMatcher() { // ...
// and
new StrictExpectations() {{
consumer.consume(with(new Delegate() { // ...
然而,这两项检查都失败了,因为当它们调用 X.getE()
时,它们收到的不是枚举值 E.B
。单元测试的输出为:
-----------------------------------------
RUNNING CHECKS ON: i=2, e=B
Direct reference match.
Reference through getter mismatch!
Ordinal through getter reference: 0
Identity hashcode from getter reference: 1282788025
Identity hashcode of expected reference: 519569038
-----------------------------------------
RUNNING CHECKS ON: i=2, e=B
Direct reference match.
Reference through getter mismatch!
Ordinal through getter reference: 0
Identity hashcode from getter reference: 1911728085
Identity hashcode of expected reference: 519569038
第一个问题是(这个post的标题):为什么JMockIt首先要干预这个getter?据我所知,我没有要求它模拟 class X,因此对其 getter 方法的调用应该进入常规 X 字节码,但显然它没有;返回的枚举值的序数为零,这似乎是模拟的默认值。这是预期的行为,因为我有 missed/misunderstood 东西,还是可能是一个错误?
第二个问题是:假设 JMockIt 的行为有正当理由,我如何才能实现我想要的并对只能通过 X 的 getter 方法获得的值执行这种检查?我如何修复此代码以告诉 JMockIt 简单地离开 class X
。
这是将要编译的完整代码段 运行(也在 http://pastebin.com/YRL7Pdzv),我的 [= 中有 junit-4.12、hamcrest-core-1.3 和 jmockit-1.14 60=]路径,并使用 Oracle JDK 1.7.0_51.
package demonstrate;
import mockit.Delegate;
import mockit.Mocked;
import mockit.NonStrictExpectations;
import mockit.StrictExpectations;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.Before;
import org.junit.Test;
public class SomeTest {
@Mocked
Factory factory;
@Mocked
Consumer consumer;
@Before
public void setUp() {
new NonStrictExpectations() {{
factory.create(anyInt, (E) any);
result = new Delegate() {
@SuppressWarnings("unused")
public X create(int i, E e) {
return new X(i, e);
}
};
}};
}
@Test
public void testWithMatcher() {
System.err.println("-----------------------------------------");
new StrictExpectations() {{
consumer.consume(withArgThat(new BaseMatcher() {
final int i = 2; final E e = E.B;
@Override
public boolean matches(Object item) {
return runChecks((X) item, i, e);
}
@Override
public void describeTo(Description description) {
description.appendText("\"i=" + i + ", e=" + e.name() + "\"");
}
}));
}};
new Service(factory, consumer).interact(2, E.B);
}
@Test
public void testWithDelegate() {
System.err.println("-----------------------------------------");
new StrictExpectations() {{
consumer.consume(with(new Delegate() {
@SuppressWarnings("unused")
public boolean check(X x) {
return runChecks(x, 2, E.B);
}
}));
}};
new Service(factory, consumer).interact(2, E.B);
}
private static boolean runChecks(X actual, int i, E e) {
System.err.println("RUNNING CHECKS ON: " + actual);
if (actual.getI() != i) {
System.err.println("Primitive int mismatch!");
return false;
}
if (actual.e == e) {
System.err.println("Direct reference match.");
} else {
System.err.println("Reference mismatch!");
return false;
}
E otherE = actual.getE();
if (otherE != e) {
System.err.println("Reference through getter mismatch!");
System.err.println("Ordinal through getter reference: "
+ otherE.ordinal());
System.err.println("Identity hashcode from getter reference: "
+ System.identityHashCode(otherE));
System.err.println("Identity hashcode of expected reference: "
+ System.identityHashCode(e));
return false;
}
return true;
}
public enum E {
A, B
}
public static class X {
private final int i;
public final E e;
public X(int i, E e) {
this.i = i;
this.e = e;
}
@Override
public String toString() {
return "i=" + i + ", e=" + e.name();
}
public int getI() {
return i;
}
public E getE() {
return e;
}
}
public static class Factory {
public X create(int i, E e) {
return new X(i, e);
}
}
public static class Consumer {
public void consume(X arg) {
}
}
public static class Service {
private final Factory f;
private final Consumer c;
public Service(Factory f, Consumer c) {
super();
this.f = f;
this.c = c;
}
public void interact(int i, E e) {
X v = f.create(i, e);
c.consume(v);
}
}
}
我在 jmockit-users 列表中被告知这实际上是一个错误。
建议的解决方法是使用 @Mocked(cascading = false) Factory factory;
,确实可以解决问题。
注意:问题末尾有完整的代码片段
我有一个 JMockIt 测试 class Service
有两个依赖项(片段中的 Factory
和 Consumer
)。该服务在内部向 Factory
依赖项请求 class X
的 object,然后将 object 传递给此方法中的 Consumer
依赖项:
public void interact(int i, E e) {
X v = f.create(i, e);
c.consume(v);
}
在我的测试中,我想使用 Service.interact()
方法和 postulate 严格期望消费者应收到满足特定属性的参数值。现在,由于 X
类型的参数是一个没有实现 equals() 的 POJO,我想使用它提供的 getter 方法来执行我的检查; X
看起来像这样(问题末尾有完整的片段):
public static class X {
private final int i;
public final E e;
// ... see below for full snippet
public int getI() { return i; }
public E getE() { return e; } // E is an enum
所以我所做的是尝试检查消费者是否收到了 X.getI() == 2
和 X.getE() == E.B
的值,我使用 with(Delegate<X>)
和 withArgThat(Matcher<X>)
来执行此操作在我的期望中:
new StrictExpectations() {{
consumer.consume(withArgThat(new BaseMatcher() { // ...
// and
new StrictExpectations() {{
consumer.consume(with(new Delegate() { // ...
然而,这两项检查都失败了,因为当它们调用 X.getE()
时,它们收到的不是枚举值 E.B
。单元测试的输出为:
----------------------------------------- RUNNING CHECKS ON: i=2, e=B Direct reference match. Reference through getter mismatch! Ordinal through getter reference: 0 Identity hashcode from getter reference: 1282788025 Identity hashcode of expected reference: 519569038 ----------------------------------------- RUNNING CHECKS ON: i=2, e=B Direct reference match. Reference through getter mismatch! Ordinal through getter reference: 0 Identity hashcode from getter reference: 1911728085 Identity hashcode of expected reference: 519569038
第一个问题是(这个post的标题):为什么JMockIt首先要干预这个getter?据我所知,我没有要求它模拟 class X,因此对其 getter 方法的调用应该进入常规 X 字节码,但显然它没有;返回的枚举值的序数为零,这似乎是模拟的默认值。这是预期的行为,因为我有 missed/misunderstood 东西,还是可能是一个错误?
第二个问题是:假设 JMockIt 的行为有正当理由,我如何才能实现我想要的并对只能通过 X 的 getter 方法获得的值执行这种检查?我如何修复此代码以告诉 JMockIt 简单地离开 class X
。
这是将要编译的完整代码段 运行(也在 http://pastebin.com/YRL7Pdzv),我的 [= 中有 junit-4.12、hamcrest-core-1.3 和 jmockit-1.14 60=]路径,并使用 Oracle JDK 1.7.0_51.
package demonstrate; import mockit.Delegate; import mockit.Mocked; import mockit.NonStrictExpectations; import mockit.StrictExpectations; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.junit.Before; import org.junit.Test; public class SomeTest { @Mocked Factory factory; @Mocked Consumer consumer; @Before public void setUp() { new NonStrictExpectations() {{ factory.create(anyInt, (E) any); result = new Delegate() { @SuppressWarnings("unused") public X create(int i, E e) { return new X(i, e); } }; }}; } @Test public void testWithMatcher() { System.err.println("-----------------------------------------"); new StrictExpectations() {{ consumer.consume(withArgThat(new BaseMatcher() { final int i = 2; final E e = E.B; @Override public boolean matches(Object item) { return runChecks((X) item, i, e); } @Override public void describeTo(Description description) { description.appendText("\"i=" + i + ", e=" + e.name() + "\""); } })); }}; new Service(factory, consumer).interact(2, E.B); } @Test public void testWithDelegate() { System.err.println("-----------------------------------------"); new StrictExpectations() {{ consumer.consume(with(new Delegate() { @SuppressWarnings("unused") public boolean check(X x) { return runChecks(x, 2, E.B); } })); }}; new Service(factory, consumer).interact(2, E.B); } private static boolean runChecks(X actual, int i, E e) { System.err.println("RUNNING CHECKS ON: " + actual); if (actual.getI() != i) { System.err.println("Primitive int mismatch!"); return false; } if (actual.e == e) { System.err.println("Direct reference match."); } else { System.err.println("Reference mismatch!"); return false; } E otherE = actual.getE(); if (otherE != e) { System.err.println("Reference through getter mismatch!"); System.err.println("Ordinal through getter reference: " + otherE.ordinal()); System.err.println("Identity hashcode from getter reference: " + System.identityHashCode(otherE)); System.err.println("Identity hashcode of expected reference: " + System.identityHashCode(e)); return false; } return true; } public enum E { A, B } public static class X { private final int i; public final E e; public X(int i, E e) { this.i = i; this.e = e; } @Override public String toString() { return "i=" + i + ", e=" + e.name(); } public int getI() { return i; } public E getE() { return e; } } public static class Factory { public X create(int i, E e) { return new X(i, e); } } public static class Consumer { public void consume(X arg) { } } public static class Service { private final Factory f; private final Consumer c; public Service(Factory f, Consumer c) { super(); this.f = f; this.c = c; } public void interact(int i, E e) { X v = f.create(i, e); c.consume(v); } } }
我在 jmockit-users 列表中被告知这实际上是一个错误。
建议的解决方法是使用 @Mocked(cascading = false) Factory factory;
,确实可以解决问题。