Java Easymock жалуется на java.lang.IllegalStateException: метод void не может вернуть значение или последний вызов доступного макета

Мы используем EasyMock для тестирования JUnit нашего Java-приложения внутри Eclipse. Используя код, аналогичный приведенному ниже, мы обнаружили странное поведение: при запуске полного набора тестов (Eclipse Project -> Run as -> JUnit) один тестовый пример повторяется с ошибкой. Однако, когда он работает автономно, он работает нормально.

Интерфейс:

package de.zefiro.java.easymockexception;

public interface Fruit {
    public String fall();
}

Тестовый класс:

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"));
    }
}

Тестирование:

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 { }

Выполнение всех тестов в проекте Eclipse, т. е. как ScienceTests, вызывающих Newton, так и Newton напрямую, привело к этому исключению в приведенном выше небольшом примере:

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

Здесь есть похожий вопрос, но это, кажется, не связано.

А в нашем реальном тестовом коде (класс побольше, но главные действующие лица идентичны урезанному примеру) это исключение:

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

Я не нашел ответа ни в Google, ни здесь, в StackOverflow, но нашел сам сейчас, так что в духе отвечая на ваши собственные вопросы Я опубликую свои выводы ниже. Стоит также упомянуть этот пост, который я нашел, хотя он не помог мне в этом конкретном случае: Причинно-следственное сопоставление исключений EasyMock


person Zefiro    schedule 10.04.2012    source источник
comment
Я скопировал ваш код в тестовый проект в Eclipse, и и пакет, и тест прошли нормально. Не знаете, как воспроизвести ошибку? Использование статического блока кода вместо @BeforeClass кажется мне запахом кода.   -  person Ryan Nelson    schedule 10.04.2012
comment
Я только что попробовал только что загруженный Eclipse (Indigo Service Release 2), JUnit (4.10) и EasyMock (3.1), и при запуске всех тестов в проектах (Package Explorer, щелчок правой кнопкой мыши по проекту, запуск от имени, тесты JUnit) он будет запустите testGravity дважды, во второй раз потерпите неудачу.   -  person Zefiro    schedule 11.04.2012
comment
Я согласен, что немного меняю поведение. Я не уверен, что это уже запах кода - возможно, статическая окончательная инициализированная декларация - это часть, которую нужно изменить, а не насмешливая установка, сделанная статической. Если вы найдете лучшее решение, мне было бы интересно узнать :)   -  person Zefiro    schedule 11.04.2012


Ответы (1)


Ставя точки останова на строку, инициализирующую APPLE, и внутри SetUpClass(), я заметил, что APPLE вызывается ровно один раз, а SetUpClass вызывается дважды. Это связано с тем, что первая ссылка на Newton создает класс и запускает статические инициализаторы, однако JUnit вызывает @BeforeClass для каждого запуска теста. В этом случае тест запускается дважды: один раз как обычный вызов и один раз как часть набора тестов.

Я не хотел менять логику (т.е. не использовать static), а вместо этого изменил статический @BeforeClass на статический блок инициализации:

public class Newton {

    [...]

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

    // no @BeforeClass needed anymore

    [...]
}

Это решило проблему как в моем упрощенном тесте выше, так и в нашем реальном тестовом кодировании.

Я не обнаружил, в чем разница, которая вызвала другое сообщение об исключении, но выводы были одинаковыми - new вызывался только один раз, @BeforeClass вызывался несколько раз и терпел неудачу при втором запуске. Исправление также работало на обоих.

person Zefiro    schedule 10.04.2012