[java] Mockito로 정적 메서드 모의


Answers

사용을 피할 방법이없는 정적 메소드를 피하는 전형적인 전략은 랩핑 된 오브젝트를 작성하고 대신 랩퍼 오브젝트를 사용하는 것입니다.

래퍼 객체는 실제 정적 클래스에 대한 facades가되고 테스트하지는 않습니다.

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

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 빈을 @ApplicationScoped로 만들고, 공동 작업자 (테스트를 위해 지저분한 생성자조차 필요하지 않음)로 주입 한 다음 조롱을 계속합니다.

Question

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를 사용하고 있습니다. 이 특정 유스 케이스를 조롱하거나 검증하는 좋은 방법이 있습니까?







약간의 리팩토링으로이 작업을 수행 할 수 있습니다.

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);
    }
}



나는 비슷한 문제가 있었다. @PrepareForTest(TheClassYouWriteTestFor.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());
    }
}



Links