Mock Runtime.getRuntime()?
Может ли кто-нибудь сделать какие-либо предложения о том, как лучше всего использовать EasyMock для ожидания вызова Runtime.getRuntime().exec(xxx)
?
Я мог бы переместить вызов в метод в другом классе, который реализует интерфейс, но скорее не в идеальном мире.
interface RuntimeWrapper {
ProcessWrapper execute(String command) throws IOException;
}
interface ProcessWrapper {
int waitFor() throws InterruptedException;
}
Мне было интересно, есть ли у кого-нибудь другие предложения?
Ответы
Ответ 1
Ваш класс не должен называть Runtime.getRuntime()
. он должен ожидать, что Runtime
будет установлен как его зависимость и будет работать с ним. Затем в вашем тесте вы легко можете дать макет и установить его как зависимость.
В качестве побочного предложения я предлагаю посмотреть эту лекцию о дизайне OO для тестирования.
Обновление: Я не видел частный конструктор. Вы можете попробовать использовать java байт-код инструментария, чтобы добавить другого конструктора или сделать конструктор общедоступным, но это тоже может оказаться невозможным (если есть некоторые ограничения на этот класс).
Итак, ваш вариант состоит в том, чтобы сделать обертку (как вы сказали в вопросе) и следовать подходу, основанному на зависимостях.
Ответ 2
Божо выше - это ИМО Правильное решение. Но это не единственное решение. Вы можете использовать PowerMock или JMockIt,
Использование PowerMock:
package playtest;
public class UsesRuntime {
public void run() throws Exception {
Runtime rt = Runtime.getRuntime();
rt.exec("notepad");
}
}
package playtest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.legacy.PowerMockRunner;
import static org.powermock.api.easymock.PowerMock.*;
import static org.easymock.EasyMock.expect;
@RunWith(PowerMockRunner.class)
@PrepareForTest( { UsesRuntime.class })
public class TestUsesRuntime {
@Test
public void test() throws Exception {
mockStatic(Runtime.class);
Runtime mockedRuntime = createMock(Runtime.class);
expect(Runtime.getRuntime()).andReturn(mockedRuntime);
expect(mockedRuntime.exec("notepad")).andReturn(null);
replay(Runtime.class, mockedRuntime);
UsesRuntime sut = new UsesRuntime();
sut.run();
}
}
Ответ 3
Возможно, вместо издевательств Runtime.getRuntime().exec()
вы могли бы "высмеять" script/program/etc. он должен был звонить.
Вместо передачи реальной строки командной строки в exec()
, напишите тест script и запустите его. Вы можете получить script возвращаемые жестко запрограммированные значения, которые вы могли бы протестировать так же, как издевавшийся класс.
Ответ 4
Вот как вы это сделаете с EasyMock 3.0 (и JUnit 4):
import org.junit.*;
import org.easymock.*;
import static org.easymock.EasyMock.*;
public final class EasyMockTest extends EasyMockSupport
{
@Test
public void mockRuntimeExec() throws Exception
{
Runtime r = createNiceMock(Runtime.class);
expect(r.exec("command")).andReturn(null);
replayAll();
// In tested code:
r.exec("command");
verifyAll();
}
}
Единственная проблема с вышеприведенным тестом заключается в том, что объект Runtime
должен быть передан в тестируемый код, что предотвращает его использование Runtime.getRuntime()
.
С JMockit, с другой стороны, следующий тест можно записать, избегая этой проблемы:
import org.junit.*;
import mockit.*;
public final class JMockitTest
{
@Test
public void mockRuntimeExec() throws Exception
{
final Runtime r = Runtime.getRuntime();
new NonStrictExpectations(r) {{ r.exec("command"); times = 1; }};
// In tested code:
Runtime.getRuntime().exec("command");
}
}