[java] 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