为什么 JMockIt 在此代码段中模拟 getter X.getE() for class X?

Why does JMockIt mock the getter X.getE() for class X in this snippet?

注意:问题末尾有完整的代码片段

我有一个 JMockIt 测试 class Service 有两个依赖项(片段中的 FactoryConsumer)。该服务在内部向 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() == 2X.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;,确实可以解决问题。