2010-02-13 2 views
13

누구나 EasyMock을 사용하여 Runtime.getRuntime().exec(xxx)으로 전화하는 것이 가장 좋은 방법에 대해 제안 할 수 있습니까?Mock Runtime.getRuntime()?

인터페이스를 구현하는 다른 클래스의 메서드로 호출을 이동할 수 있지만 이상적인 세계에서는 호출하지 않을 수 있습니다.

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

interface ProcessWrapper { 
    int waitFor() throws InterruptedException; 
} 

다른 제안이 있다면 궁금하십니까?

답변

13

클래스는 Runtime.getRuntime()으로 전화하지 않아야합니다. Runtime이 종속성으로 설정되어 작업해야합니다. 그런 다음 테스트에서 쉽게 모의 객체를 제공하고 의존 객체로 설정할 수 있습니다.

사이드 노트로, 나는 this lecture on OO Design for testability을 보길 권합니다.

업데이트 : 개인 생성자가 표시되지 않았습니다. 다른 생성자를 추가하거나 생성자를 public으로 만들려면 java bytecode instrumentation을 사용할 수 있지만 불가능할 수도 있습니다 (해당 클래스에 제한이있는 경우).

귀하의 옵션은 (질문에서 제안한 것처럼) 래퍼를 만들고 종속성 삽입 방식을 따르는 것입니다.

+1

감사 : JMockit로 , 다른 한편으로는, 다음과 같은 시험은 그 문제를 피하고, 기록 할 수 있습니다. 그러나, 런타임의 조롱 된 인스턴스를 얻을 수있는 방법을 볼 수 없습니다 - 그것은 인터페이스가 아니며 전용 생성자가 있기 때문에 하위 클래스 수 있는지 잘 모르겠습니다. 아마도 나는 뭔가를 놓친거야? – Rich

+0

넵, 그건 불가능하게 만듭니다. 내 업데이트를 확인하십시오. – Bozho

+0

래퍼 접근 방식으로 갈 것입니다 :) 다시 한번 감사드립니다! – Rich

0

아마도 Runtime.getRuntime().exec()을 조롱하는 대신 스크립트/프로그램/등을 "조롱 할"수 있습니다. 부름을 받는다.

exec()에 실제 명령 줄 문자열을 전달하는 대신 테스트 스크립트를 작성하고 대신 실행하십시오. 스크립트가 조롱 된 클래스처럼 테스트 할 수있는 하드 코딩 된 값을 반환하도록 할 수 있습니다.

+0

그게 내가 처음에 시도한거야,하지만 그걸로 몇 가지 문제를 발견. 무엇보다 먼저 테스트의 플랫폼 독립성이 깨졌습니다 (코드가 Windows 용으로 설계되었지만 테스트가 Linux 상자에서 실행되는 경우도 있음). 둘째, 어떤 이유로 스크립트가 조롱당하는 것을 두려워합니다. 아마 나는 그것을 검사하는 것을 두려워하기 때문에 :) 또한 모의 실행 시간을 통해 다른 시나리오를보다 쉽게 ​​시뮬레이션 할 수 있습니다. 어쨌든 고마워! – Rich

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(); 
    } 
} 
+0

제안에 감사드립니다. 전에 Powermock에 대해 들어 보지 못했습니다. – Rich

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()을 사용할 수 없습니다. 내가 종속성을 주입하는 것이 가장 좋은 방법이라고 동의하지만, 나는 그것을 조롱하는 것을 선호 - 제안에 대한

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"); 
    } 
} 
관련 문제