简体   繁体   中英

Runtime.getRuntime().exec jUnit test

I have a class that in some of its methods is using

Runtime.getRuntime().exec ...

For example:

public class MyClass {
    public void doSomething() {
        ...do something...
        Runtime.getRuntime().exec ...
        ...do something else
    }
}

Unfortunately I "cannot refactor" the class due to some requirements. I want to create jUnit tests on this class and I'm finding it hard to mock the Runtime class.

Let's say I want to test the "doSomething" method in the cases where Runtime process returns the X result or the Y result. Is there any way to mock it?

You can do that using PowerMockito and mockStatic method.

The idea is to mock the static Runtime.getRuntime() method to return a mocked runtime object, and on that you can control the outcome of exec()

@RunWith(PowerMockRunner.class)
@PrepareForTest(Runtime.class)
public class TestClass {

  @Mock private Runtime mockRuntime;

  @Test
  public void test() {
    PowerMockito.mockStatic(Runtime.class);

    when(Runtime.getRuntime()).thenReturn(mockRuntime);
    when(mockRuntime.exec()).thenReturn("whatever you want");

    // do the rest of your test
  }
}

A working example :

In src/main/java/sandbox/xx , XX.java

package sandbox.xx;

import java.io.File;
import java.io.IOException;

class XX {
    Process run(final String command) throws IOException {

        return this.run(command, null, null);
    }

    Process run(final String command, final String[] envp) throws IOException {

        return this.run(command, envp, null);
    }

    Process run(final String command, final String[] envp, final File dir) throws IOException {

        return Runtime.getRuntime().exec(command, envp, dir);
    }

    Process run(final String[] cmdarray) throws IOException {

        return this.run(cmdarray, null, null);
    }

    Process run(final String[] cmdarray, final String[] envp) throws IOException {

        return this.run(cmdarray, envp, null);
    }

    Process run(final String[] cmdarray, final String[] envp, final File dir) throws IOException {

        return Runtime.getRuntime().exec(cmdarray, envp, dir);
    }
}

In src/test/java/sandbox/xx , XXTest.java

package sandbox.xx;

import java.io.IOException;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(XX.class) // watch it: not @PrepareForTest(Runtime.class)!
public class XXTest {

    private static final String TRIGGER_ARG = "some arg";
    private static final String EXPECTED_PREFIX = "gotcha!";
    private static final String COLON = ": ";
    private static final String EXPECTED_RESULT = EXPECTED_PREFIX + COLON + TRIGGER_ARG;

    @Test
    public void test() throws IOException {

        final Runtime mockRuntime = PowerMockito.mock(Runtime.class);
        PowerMockito.mockStatic(Runtime.class);

        Mockito.when(Runtime.getRuntime()).thenReturn(mockRuntime);
        Mockito.when(mockRuntime.exec(ArgumentMatchers.eq(TRIGGER_ARG), ArgumentMatchers.any(), ArgumentMatchers.any())).thenAnswer(invocation -> {
            final Process mockProcess = PowerMockito.mock(Process.class);
            Mockito.when(mockProcess.toString()).thenReturn(EXPECTED_PREFIX + COLON + invocation.getArguments()[0]);
            return mockProcess;
        });

        final XX xx = new XX();
        Assert.assertEquals(EXPECTED_RESULT, xx.run(TRIGGER_ARG).toString());
        Assert.assertNull(xx.run("some other arg"));
    }
}

Hope this helps

Mocking Runtime is always tricky specially if it is a complex project. I did that before but it was almost impossible to stick to one Mocking framework. So I used Easymock for the project classes and the Powermock for Runtime. But it was really complex and not easily readable for the next developers. So my suggestion is keep one class in your project which calls exactly one Runtime method ( for you it is exec, likewise if you want to call halt() method, keep that in separate class). Then use Mockito for that class using Runtime method and verify the same mock. Good Luck!

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