Mock Runtime.getRuntime ()? - PullRequest
       23

Mock Runtime.getRuntime ()?

15 голосов
/ 13 февраля 2010

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

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

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

interface ProcessWrapper {
    int waitFor() throws InterruptedException;
}

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

Ответы [ 4 ]

14 голосов
/ 13 февраля 2010

Ваш класс не должен звонить Runtime.getRuntime(). он должен ожидать, что Runtime будет установлен как его зависимость, и работать с ним. Затем в своем тесте вы можете легко предоставить макет и установить его в качестве зависимости.

Как примечание, я бы посоветовал посмотреть эту лекцию о OO Design для тестируемости .

Обновление: Я не видел приватного конструктора. Вы можете попробовать использовать инструментарий java-байт-кода , чтобы добавить другой конструктор или сделать его общедоступным, но это также может оказаться невозможным (если существуют некоторые ограничения для этого класса).

Таким образом, вы можете создать оболочку (как вы предложили в вопросе) и следовать подходу внедрения зависимостей.

6 голосов
/ 15 февраля 2010

Божо выше ИМО Правильное решение . Но это не единственное решение. Вы можете использовать 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();
    }
}
1 голос
/ 13 февраля 2010

Возможно, вместо насмешки Runtime.getRuntime().exec() вы могли бы "насмехаться" над сценарием / программой / и т. Д. он должен звонить.

Вместо передачи действительной строки командной строки в exec(), напишите тестовый сценарий и выполните его вместо этого. Вы можете сделать так, чтобы скрипт возвращал жестко запрограммированные значения, с которыми можно было бы протестировать, как и в случае с поддельным классом.

0 голосов
/ 30 августа 2010

Вот как вы бы это сделали с 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");
    }
}
...