1. 在使用PowerMockito之前,首先要了解到Mockito这个测试工具,因为PowerMockito就是基于Mockito增强了一些功能,比如静态方法的测试。这里的可以参考:基于Mockito的多层模拟单元测试 小结
2. 网上有很多PowerMockito mock静态方法的总结,以下主要是针对这几天使用过程中遇到的各种问题,从mock公共静态方法、私有方法两个方面进行总结。
3. 首先先是PowerMockito的pom.xml的依赖。
4.12
2.19.0
2.0.0-beta.5
org.mockito
mockito-core
${mockito-core.version}
junit
junit
${junit4.version}
test
org.powermock
powermock-module-junit4
${powermock.version}
test
org.powermock
powermock-api-mockito2
${powermock.version}
test
注:因为PowerMockito是根据Mockito进行增强的,相应版本依赖要求比较严格。
如果遇到:java.lang.NoSuchMethodError: org.mockito.internal.handler.MockHandlerFactory.createMockHandler... 就是pom.xml里面的依赖版本不对了。
4. PowerMockito mock私有方法。
1) 假设以下的S3Util需要被mock测试。
public class S3Util {
public S3Util() {
}
private void call(String output) {
System.out.println(output);
}
public ByteBuffer getRandomByteBuffer(int size) {
byte[] b = new byte[size];
new Random().nextBytes(b);
call("123123123123");
System.out.println(222);
return ByteBuffer.wrap(b);
}
}
2) 测试,调用public方法。
@RunWith(PowerMockRunner.class)
@PrepareForTest({S3Util.class})
@PowerMockIgnore({"javax.management.*", "javax.net.ssl.*"})
public class S3UtilTest {
@Mock
S3Util s3UtilMock;
@Mock
S3Client s3Client;
@Before
public void before() {
PowerMockito.mockStatic(S3Util.class);
}
@Test
public void test () throws Exception {
S3Util s3Util = PowerMockito.spy(new S3Util());
PowerMockito.doNothing().when(s3Util, "call", anyString());
s3Util.getRandomByteBuffer(1);
}
}
输出结果(private方法被mock):
5. PowerMockito mock公有静态方法。
1) 有返回值的公有静态方法。
a) 返回String类型。
public static String getFolder(String yyyyMMdd) {
return "data_date=" + yyyyMMdd + "/";
}
@Test
public void testGetFolder() {
PowerMockito.when(S3Util.getFolder("20200616")).thenReturn("data_date=20200616/");
assertEquals("data_date=20200616/", S3Util.getFolder("20200616"));
}
b) 返回Boolean类型。
public static Boolean copyObject(S3Client s3, String fromBucket, String toBucket, String fromKey, String toKey, boolean skipFileNotFound) {
String encodedUrl = null;
try {
encodedUrl = URLEncoder.encode(fromBucket + "/" + fromKey, StandardCharsets.UTF_8.toString());
} catch (UnsupportedEncodingException e) {
System.out.printf("URL could not be encoded: %s", e.getMessage());
return Boolean.FALSE;
}
CopyObjectRequest copyReq = CopyObjectRequest.builder()
.copySource(encodedUrl)
.bucket(toBucket)
.key(toKey)
.build();
try {
CopyObjectResponse copyRes = s3.copyObject(copyReq);
System.out.printf("copyObject successfully: from %s , to %s\n", fromKey, toKey);
} catch (S3Exception e) {
if (e.statusCode() == 404 && skipFileNotFound) {
System.out.printf("copyObject 404: %s in %s", fromKey, fromBucket);
return Boolean.TRUE;
}
System.out.printf("copyObject s3Exception: %s", e);
return Boolean.FALSE;
}
return Boolean.TRUE;
}
@Test
public void testCopyObject() {
PowerMockito.when(S3Util.copyObject(Mockito.any(S3Client.class), anyString(), anyString(), anyString(), anyString(), anyBoolean())).thenReturn(true);
assertEquals(true, S3Util.copyObject(s3Client, "fromBucket", "toBucket", "fromKey", "toKey", false));
}
c) 返回List类型。
public static List listObjOfBucket(S3Client s3, String bucket, String path) {
ListObjectsV2Request listReq = ListObjectsV2Request.builder()
.bucket(bucket)
.prefix(path)
.maxKeys(1)
.build();
ListObjectsV2Iterable listRes = s3.listObjectsV2Paginator(listReq);
List result = new ArrayList<>();
SdkIterable sdkIterable = listRes.contents();
for (S3Object content : sdkIterable) {
if (content.size() == 0)
continue;
if (null != path) {
if (content.key().startsWith(path))
result.add(content);
} else {
result.add(content);
}
}
return result;
}
@Test
public void testListObjOfBucket() {
PowerMockito.when(S3Util.listObjOfBucket(Mockito.any(S3Client.class), anyString(), anyString())).thenReturn(new ArrayList());
assertNotNull(S3Util.listObjOfBucket(s3Client, "bucketName", "path"));
}
d) 返回Object类型。
public static S3Client builder(String regionStr) {
Region region = Region.US_EAST_1;//default region
if (StringUtils.isNotEmpty(regionStr)) {
region = Region.of(regionStr);
}
return S3Client.builder().region(region).build();
}
public static S3Client builder(Credentials credentials) {
Region region = Region.US_EAST_1;//default region
AwsSessionCredentials awsCreds = AwsSessionCredentials.create(
credentials.accessKeyId(),
credentials.secretAccessKey(),
credentials.sessionToken());
return S3Client.builder().credentialsProvider(StaticCredentialsProvider.create(awsCreds)).region(region).build();
}
@Test
public void testBuilder() {
PowerMockito.when(S3Util.builder(anyString())).thenReturn(S3Client.builder().build());
assertNotNull(S3Util.builder("us-east-1"));
PowerMockito.when(S3Util.builder(Mockito.any(Credentials.class))).thenReturn(S3Client.builder().build());
assertNotNull(S3Util.builder(Credentials.builder().build()));
}
e) 返回byte[]类型。
public static byte[] getS3FileToBytes(S3Client s3, String bucket, String multipartKey) {
return s3.getObjectAsBytes(GetObjectRequest.builder().bucket(bucket).key(multipartKey).build()).asByteArray();
}
@Test
public void testGetS3FileToBytes() throws Exception {
byte[] bytes = new byte[]{};
PowerMockito.when(S3Util.class, "getS3FileToBytes", Mockito.any(S3Client.class), anyString(), anyString()).thenReturn(bytes);
assertEquals(bytes, S3Util.getS3FileToBytes(s3Client, "bucketName", "multipartKey"));
}
2) 没有返回值的公有静态方法。
public static void putObject(S3Client s3, String bucket, String key, byte[] bytes) {
s3.putObject(PutObjectRequest.builder().bucket(bucket).key(key).build(),
RequestBody.fromByteBuffer(ByteBuffer.wrap(bytes)));
}
@Test
public void testPutObject() throws Exception {
// PowerMockito.doNothing().when(S3Util.class, "putObject", Mockito.any(S3Client.class), anyString(), anyString() , anyString());
PowerMockito.doNothing().when(S3Util.class);
S3Util.putObject(Mockito.any(S3Client.class), anyString(), anyString(), anyString());
S3Util.putObject(Mockito.any(S3Client.class), anyString(), anyString(), Mockito.any(byte[].class));
}
注:
1. 以上的mock方法还可以是:Mockito.doAnswer((invocationOnMock) -> 返回值).when(mock对象).方法名(参数列表);
2. 如果遇到:org.apache.http.conn.ssl.SSLInitializationException: class configured for SSLContext: sun.security.ssl.SSLContextImpl...
在unit-test上面添加:@PowerMockIgnore({"javax.net.ssl.*"})。
3. 如果遇到:ERROR Could not reconfigure JMX java.lang.LinkageError: loader constraint violation: loader...
在unit-test上面添加:@PowerMockIgnore({"javax.management.*"})。
附: 以上demo的源码下载链接。