简体   繁体   中英

Java Easymock complains with "java.lang.IllegalStateException: void method cannot return a value" or "no last call on a mock available"

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 - ie 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

Putting Breakpoints on the line initializing APPLE and inside SetUpClass() I noticed that APPLE is called exactly once, while SetUpClass is called twice. This is due to the fact that the first reference to Newton creates the class and runs the static initializers, however JUnit calls @BeforeClass for each run of the test. In this case the test is run twice: once as a normal call and once as part of the test suite.

I didn't want to change the logic (ie don't use static), but instead changed the static @BeforeClass to a static initialization block:

public class Newton {

    [...]

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

    // no @BeforeClass needed anymore

    [...]
}

This solved the issue in both my simplified test above and in our real test coding.

I didn't find out what the difference was that triggered the different exception message, but the findings were the same - new was called only once, @BeforeClass was called multiple times and failed on the second run. The fix also worked on both.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM