2

We are using EasyMock for JUnit testing of our Java application inside Eclipse. Using code similar to the below, we found a strange behaviour: when running the full test suite (Eclipse Project -> Run as -> JUnit) one test case fails reproducibly. However when running it standalone it works fine.

Interface:

package de.zefiro.java.easymockexception;

public interface Fruit {
    public String fall();
}

Test class:

package de.zefiro.java.easymockexception;

import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.junit.Assert.assertTrue;

import org.junit.BeforeClass;
import org.junit.Test;

public class Newton {
    private static final Fruit APPLE = createNiceMock(Fruit.class);

    @BeforeClass
    public static void SetUpClass() {
        expect(APPLE.fall()).andReturn("Targeting HEAD").anyTimes();
        replay(APPLE);
    }

    @Test
    public void testGravity() {
        String target = APPLE.fall();
        assertTrue("Missed", target.contains("HEAD"));
    }
}

Test suite:

package de.zefiro.java.easymockexception;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(value = Suite.class)
@SuiteClasses( { Newton.class } )
public class ScienceTests { }

Running all tests on the Eclipse project - i.e. both ScienceTests calling Newton as well as Newton directly - produced this exception in the above small example:

java.lang.IllegalStateException: no last call on a mock available
at org.easymock.Easymock.getControlForLastCall(EasyMock.java:175)

There is a similar question here, but it seems to be unrelated.

And in our real testing code (bigger class, but the main actors are identical to the stripped-down example) this exception:

java.lang.IllegalStateException: void method cannot return a value
at org.easymock.internal.MocksControl.andReturn(MocksControl.java:101)

I didn't find an answer either on Google nor here on StackOverflow, but found out myself now, so in the spirit of answering your own questions I'll post my findings below. Worth mentioning is also this post I found, even though it didn't help me in this particular case: EasyMock Cause-Effect Exception Mapping

4

1 回答 1

0

将断点放在初始化 APPLE 的行和 SetUpClass() 中,我注意到 APPLE 只被调用了一次,而 SetUpClass 被调用了两次。这是因为对 Newton 的第一次引用会创建类并运行静态初始化程序,但是 JUnit 会在每次运行测试时调用 @BeforeClass。在这种情况下,测试运行两次:一次作为正常调用,一次作为测试套件的一部分。

我不想更改逻辑(即不使用静态),而是将静态 @BeforeClass 更改为静态初始化块:

public class Newton {

    [...]

    static {
        expect(APPLE.fall()).andReturn("Targeting HEAD").anyTimes();
        replay(APPLE);
    }

    // no @BeforeClass needed anymore

    [...]
}

这解决了我上面的简化测试和我们的真实测试编码中的问题。

我没有发现触发不同异常消息的区别是什么,但结果是相同的 - new 只被调用一次,@BeforeClass 被多次调用并在第二次运行时失败。该修复程序也适用于两者。

于 2012-04-10T17:08:09.137 回答