Подтвердить что ты не робот

Mock Runtime.getRuntime()?

Может ли кто-нибудь сделать какие-либо предложения о том, как лучше всего использовать EasyMock для ожидания вызова Runtime.getRuntime().exec(xxx)?

Я мог бы переместить вызов в метод в другом классе, который реализует интерфейс, но скорее не в идеальном мире.

interface RuntimeWrapper {
    ProcessWrapper execute(String command) throws IOException;
}

interface ProcessWrapper {
    int waitFor() throws InterruptedException;
}

Мне было интересно, есть ли у кого-нибудь другие предложения?

4b9b3361

Ответ 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");
    }
}