当相同的调用是“预期一次,从未调用”时,JMock“意外调用”

时间:2012-12-12 23:47:26

标签: java jmock

我在代码中的某个地方更改了一些不应该导致任何奇怪的测试失败的方法,但JMock似乎不这么认为。

我将问题归结为最小量的问题,看起来像这样:

import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test;

public class TestMocking {

    @Test
    public void test() {
        Mockery mockery = new Mockery() {{
            setImposteriser(ClassImposteriser.INSTANCE);
        }};
        final Owner owner = mockery.mock(Owner.class);
        final RealThing thing = mockery.mock(RealThing.class, "thing");

        mockery.checking(new Expectations() {{
            oneOf(owner).getThing(); will(returnValue(thing));
            oneOf(thing).method(); will(returnValue(Collections.emptyList()));
        }});

        owner.getThing().method();

        mockery.assertIsSatisfied();
    }

    public static interface Owner {
        BaseThing getThing();
    }

    public static interface BaseThing {
        Collection<?> method();
    }

    public static interface RealThing extends BaseThing {
        List<?> method();
    }
}

编辑:现在使用ClassImposteriser,即使不再有类,因为我想证明你可以运行完全相同的代码而不使用imposteriser并且测试会通过。)< / p>

运行此结果:

unexpected invocation: thing.method()
expectations:
  expected once, already invoked 1 time: owner.getThing(); returns <thing>
  expected once, never invoked: thing.method(); returns <[]>
what happened before this:
  owner.getThing()

所以当你从未调用过期望的thing.method()时,你会去“意外”的thing.method()。我之前已经看到,当多线程类针对模拟对象进行测试时会出现这种情况,但这一次都发生在单个线程中。这就像JMock以某种方式从第一个方法调用返回一个不同的模拟对象,即使我没有模仿这样的对象。

如果我删除了返回更具体类型的重写方法,那么它就会消失,但我显然不能这样做。同样,如果我删除使用ClassImposteriser,问题就会消失,但我在真实测试中嘲笑的其中一个对象是别人的类。我想我可以尝试在一次测试中使用两个Mockery实例,但除此之外我没有想法。

1 个答案:

答案 0 :(得分:2)

隐藏类(静态)方法与重写实例方法的工作方式完全不同。为了证明JMock不应该受到责备,试试这个:

public class test3 {
public static void main(String[] args) {
    Owner owner = new Owner();
    owner.getThing().method(); //Like how you execute your test
    RealThing thing = new RealThing();
    thing.method(); //Similar to your mock.
}

private static class Owner {
    private BaseThing thing = new RealThing();

    public BaseThing getThing() {
        return thing;
    }
}

private static class BaseThing {
    public static void method() {
        System.out.println("Basething!");
    }
}

private static class RealThing extends BaseThing {
    public static void method() {
        System.out.println("Realthing!");
    }
}
}

请注意,对method()的两次调用会打印不同的内容!两者都是RealThing的实例,但它们调用不同的方法。调用的静态方法取决于它是从subcalss还是从超类调用。在上面的第一个调用中,方法被声明为BaseClass,因此调用BaseClass.method(),即使它是RealClass的实例。对method()的第二次调用被声明为RealClass,因此调用了RealClass.method()

因此,JMock的结果是有效的。调用的method()与您设置的期望值不同。

我对此的解释感觉不太好。请在此处阅读:http://docs.oracle.com/javase/tutorial/java/IandI/override.html


修复(赞成BaseThing.method()),更改:

final RealThing thing = mockery.mock(RealThing.class, "thing");

要:

final BaseThing thing = mockery.mock(RealThing.class, "thing");

或者,如果您更喜欢使用RealThing.method(),请更改:

owner.getThing().method()

要:

RealThing thing = (RealThing)owner.getThing();
thing.method();