基於PowerMockito的靜態方法的多種mock測試方式 小結

1. 在使用PowerMockito之前,首先要了解到Mockito這個測試工具,因爲PowerMockito就是基於Mockito增強了一些功能,比如靜態方法的測試。這裏的可以參考:基於Mockito的多層模擬單元測試 小結

2. 網上有很多PowerMockito mock靜態方法的總結,以下主要是針對這幾天使用過程中遇到的各種問題,從mock公共靜態方法私有方法兩個方面進行總結。

3. 首先先是PowerMockito的pom.xml的依賴。

<properties>
	<junit4.version>4.12</junit4.version>
	<mockito-core.version>2.19.0</mockito-core.version>
	<powermock.version>2.0.0-beta.5</powermock.version>
</properties>

<dependencies>
	<dependency>
		<groupId>org.mockito</groupId>
		<artifactId>mockito-core</artifactId>
		<version>${mockito-core.version}</version>
	</dependency>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>${junit4.version}</version>
		<scope>test</scope>
	</dependency>	
	<dependency>
		<groupId>org.powermock</groupId>
		<artifactId>powermock-module-junit4</artifactId>
		<version>${powermock.version}</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.powermock</groupId>
		<artifactId>powermock-api-mockito2</artifactId>
		<version>${powermock.version}</version>
		<scope>test</scope>
	</dependency>
</dependencies>

:因爲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<S3Object> listObjOfBucket(S3Client s3, String bucket, String path) {
			ListObjectsV2Request listReq = ListObjectsV2Request.builder()
			.bucket(bucket)
			.prefix(path)
			.maxKeys(1)
			.build();

	ListObjectsV2Iterable listRes = s3.listObjectsV2Paginator(listReq);
	List<S3Object> result = new ArrayList<>();
	SdkIterable<S3Object> 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<S3Object>());
	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的源碼下載鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章