任何人都可以就如何最好地使用EasyMock预期拨打Runtime.getRuntime().exec(xxx)
提出任何建议吗?
我可以将调用移动到另一个实现接口的类中的方法,但不希望在理想的世界中。
interface RuntimeWrapper {
ProcessWrapper execute(String command) throws IOException;
}
interface ProcessWrapper {
int waitFor() throws InterruptedException;
}
我想知道是否有人有任何其他建议?
答案 0 :(得分:13)
您的班级不应致电Runtime.getRuntime()
。它应该期望将Runtime
设置为其依赖项,并使用它。然后在测试中,您可以轻松提供模拟并将其设置为依赖项。
作为旁注,我建议观看this lecture on OO Design for testability。
更新:我没有看到私有构造函数。您可以尝试使用java bytecode instrumentation来添加另一个构造函数或使构造函数公开,但这可能也是不可能的(如果该类有一些限制)。
所以你的选择是制作一个包装器(如问题所示),并遵循依赖注入方法。
答案 1 :(得分:6)
Bozho上面是IMO 正确的解决方案。但它不是唯一的解决方案。您可以使用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();
}
}
答案 2 :(得分:1)
也许不是嘲笑Runtime.getRuntime().exec()
而是可以“模仿”脚本/程序/等。它应该是在呼唤。
不是将实际的命令行字符串传递给exec()
,而是编写测试脚本并执行它。您可以让脚本返回您可以测试的硬编码值,就像模拟类一样。
答案 3 :(得分:0)
以下是使用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");
}
}