3

我在代码中的某处更改了一些不应该导致任何奇怪的测试失败的方法,但 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,因为我想证明您可以在没有该冒名顶替者的情况下运行完全相同的代码并且测试将通过。)

运行结果:

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 实例,但除此之外我没有想法。

4

1 回答 1

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 的实例,但它们调用不同的方法。调用的静态方法取决于是从子类调用还是从超类调用。在上面的第一个调用中,方法被声明为 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();
于 2012-12-13T00:01:33.083 回答