Mockito로 정적 메소드 조롱

java.sql.Connection객체 를 생성하기 위해 팩토리를 작성했습니다 .

public class MySQLDatabaseConnectionFactory implements DatabaseConnectionFactory {

    @Override public Connection getConnection() {
        try {
            return DriverManager.getConnection(...);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

에 전달 된 매개 변수의 유효성을 검사하고 싶지만 DriverManager.getConnection정적 메서드를 조롱하는 방법을 모르겠습니다. 테스트 케이스에 JUnit 4와 Mockito를 사용하고 있습니다. 이 특정 사용 사례를 모의 / 확인하는 좋은 방법이 있습니까?



답변

Mockito 위에 PowerMockito 를 사용하십시오 .

예제 코드 :

@RunWith(PowerMockRunner.class)
@PrepareForTest(DriverManager.class)
public class Mocker {

    @Test
    public void shouldVerifyParameters() throws Exception {

        //given
        PowerMockito.mockStatic(DriverManager.class);
        BDDMockito.given(DriverManager.getConnection(...)).willReturn(...);

        //when
        sut.execute(); // System Under Test (sut)

        //then
        PowerMockito.verifyStatic();
        DriverManager.getConnection(...);

    }

추가 정보:


답변

사용하지 않는 정적 메소드를 피하기위한 일반적인 전략은 랩핑 된 오브젝트를 작성하고 랩퍼 오브젝트를 대신 사용하는 것입니다.

랩퍼 오브젝트는 실제 정적 클래스의 외관이되므로 테스트하지 않습니다.

래퍼 객체는 다음과 같습니다.

public class Slf4jMdcWrapper {
    public static final Slf4jMdcWrapper SINGLETON = new Slf4jMdcWrapper();

    public String myApisToTheSaticMethodsInSlf4jMdcStaticUtilityClass() {
        return MDC.getWhateverIWant();
    }
}

마지막으로 테스트중인 클래스는 예를 들어 실제 사용을위한 기본 생성자를 가짐으로써이 단일 객체를 사용할 수 있습니다.

public class SomeClassUnderTest {
    final Slf4jMdcWrapper myMockableObject;

    /** constructor used by CDI or whatever real life use case */
    public myClassUnderTestContructor() {
        this.myMockableObject = Slf4jMdcWrapper.SINGLETON;
    }

    /** constructor used in tests*/
    myClassUnderTestContructor(Slf4jMdcWrapper myMock) {
        this.myMockableObject = myMock;
    }
}

정적 메서드가있는 클래스를 직접 사용하지 않기 때문에 쉽게 테스트 할 수있는 클래스가 있습니다.

CDI를 사용하고 있고 @Inject 주석을 사용할 수 있으면 훨씬 쉽습니다. Wrapper bean을 @ApplicationScoped로 만들고 공동 작업자로 주입 한 것을 가져 오십시오 (테스트를 위해 지저분한 생성자가 필요하지 않음).


답변

나는 비슷한 문제가 있었다. PowerMock의 mockStatic 설명서@PrepareForTest(TheClassThatContainsStaticMethod.class) 에 따라 다음 과 같이 변경 할 때까지 허용 된 답변이 효과가 없었 습니다 .

그리고 나는 사용할 필요가 없습니다 BDDMockito.

내 수업:

public class SmokeRouteBuilder {
    public static String smokeMessageId() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.error("Exception occurred while fetching localhost address", e);
            return UUID.randomUUID().toString();
        }
    }
}

내 테스트 수업 :

@RunWith(PowerMockRunner.class)
@PrepareForTest(SmokeRouteBuilder.class)
public class SmokeRouteBuilderTest {
    @Test
    public void testSmokeMessageId_exception() throws UnknownHostException {
        UUID id = UUID.randomUUID();

        mockStatic(InetAddress.class);
        mockStatic(UUID.class);
        when(InetAddress.getLocalHost()).thenThrow(UnknownHostException.class);
        when(UUID.randomUUID()).thenReturn(id);

        assertEquals(id.toString(), SmokeRouteBuilder.smokeMessageId());
    }
}


답변

앞에서 언급했듯이 mockito를 사용하여 정적 메서드를 조롱 할 수 없습니다.

테스트 프레임 워크 변경이 옵션이 아닌 경우 다음을 수행 할 수 있습니다.

DriverManager에 대한 인터페이스를 생성하고이 인터페이스를 조롱 한 후 일종의 의존성 주입을 통해 주입하고 해당 모의를 확인하십시오.


답변

관찰 : 정적 엔티티 내에서 정적 메소드를 호출 할 때 @PrepareForTest에서 클래스를 변경해야합니다.

예를 들어 :

securityAlgo = MessageDigest.getInstance(SECURITY_ALGORITHM);

위의 코드에서 MessageDigest 클래스를 조롱 해야하는 경우

@PrepareForTest(MessageDigest.class)

아래와 같은 것이 있다면 :

public class CustomObjectRule {

    object = DatatypeConverter.printHexBinary(MessageDigest.getInstance(SECURITY_ALGORITHM)
             .digest(message.getBytes(ENCODING)));

}

그런 다음이 코드가있는 클래스를 준비해야합니다.

@PrepareForTest(CustomObjectRule.class)

그런 다음 방법을 조롱하십시오.

PowerMockito.mockStatic(MessageDigest.class);
PowerMockito.when(MessageDigest.getInstance(Mockito.anyString()))
      .thenThrow(new RuntimeException());


답변

약간의 리팩토링으로 할 수 있습니다.

public class MySQLDatabaseConnectionFactory implements DatabaseConnectionFactory {

    @Override public Connection getConnection() {
        try {
            return _getConnection(...some params...);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    //method to forward parameters, enabling mocking, extension, etc
    Connection _getConnection(...some params...) throws SQLException {
        return DriverManager.getConnection(...some params...);
    }
}

그런 다음 클래스 MySQLDatabaseConnectionFactory를 확장하여 조롱 된 연결을 반환하고 매개 변수에 대한 어설 션을 수행 할 수 있습니다 .

확장 클래스는 동일한 패키지에있는 경우 테스트 케이스 내에 상주 할 수 있습니다 (권장합니다)

public class MockedConnectionFactory extends MySQLDatabaseConnectionFactory {

    Connection _getConnection(...some params...) throws SQLException {
        if (some param != something) throw new InvalidParameterException();

        //consider mocking some methods with when(yourMock.something()).thenReturn(value)
        return Mockito.mock(Connection.class);
    }
}


답변

정적 방법을 조롱하려면 https://github.com/powermock/powermock/wiki/MockStatic 에서 Powermock을 사용해야합니다
. Mockito 이 기능을 제공하지 않습니다 .

mockito에 대한 좋은 기사를 읽을 수 있습니다 :
http://refcardz.dzone.com/refcardz/mockito