Skip to content

Commit 39d4471

Browse files
committed
Add api data unit tests. Perform api request codes.
1 parent 3ac539e commit 39d4471

File tree

8 files changed

+257
-59
lines changed

8 files changed

+257
-59
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ mcunittests是SpringBoot项目的一个单元测试库,支持API接口自动
6060

6161
## 示例代码 Example Codes
6262
```
63-
import io.github.json031.MCApiTests;
63+
import io.github.json031.apitests.MCApiTests;
6464
import org.junit.jupiter.api.Test;
6565
import org.springframework.boot.test.context.SpringBootTest;
6666

pom.xml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<groupId>io.github.json031</groupId>
66
<artifactId>mcunittests</artifactId>
77
<packaging>jar</packaging>
8-
<version>1.0.7</version>
8+
<version>1.0.8</version>
99
<name>mcunittests</name>
1010
<description>mcunittests是SpringBoot项目的一个单元测试库,是一个在MIT许可下分发的开源项目</description>
1111
<url>https://github.com/Json031/mcunittests</url>
@@ -40,6 +40,11 @@
4040
<version>5.13.0-M2</version>
4141
<scope>compile</scope>
4242
</dependency>
43+
<dependency>
44+
<groupId>com.fasterxml.jackson.core</groupId>
45+
<artifactId>jackson-databind</artifactId>
46+
<version>2.15.0</version>
47+
</dependency>
4348
</dependencies>
4449

4550
<licenses>
@@ -58,7 +63,6 @@
5863
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
5964
</properties>
6065

61-
<!-- 只保留 GitHub Packages 配置 -->
6266
<distributionManagement>
6367
<repository>
6468
<id>github</id>

src/main/java/io/github/json031/JavaBean/HighConcurrencyResult.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,24 @@
11
package io.github.json031.JavaBean;
22

3+
/**
4+
* High Concurrency Result.
5+
*/
36
public class HighConcurrencyResult {
7+
/**
8+
* total request times.
9+
*/
410
public final int total;
11+
/**
12+
* request success times.
13+
*/
514
public final int success;
15+
/**
16+
* request failed times.
17+
*/
618
public final int failed;
19+
/**
20+
* request average cost timeMillis.
21+
*/
722
public final long avgResponseTimeMillis;
823

924
public HighConcurrencyResult(int total, int success, int failed, long avgResponseTimeMillis) {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package io.github.json031.JavaBean;
2+
3+
import org.springframework.http.ResponseEntity;
4+
5+
/**
6+
* Request Unit Tests Result.
7+
*/
8+
public class RequestUnitTestsResult {
9+
// 毫秒级耗时
10+
public final long durationMillis;
11+
// api请求响应结果
12+
public final ResponseEntity<String> response;
13+
14+
public RequestUnitTestsResult(long durationMillis, ResponseEntity<String> response) {
15+
this.durationMillis = durationMillis;
16+
this.response = response;
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
package io.github.json031;
1+
package io.github.json031.apitests;
22

3+
import io.github.json031.JavaBean.RequestUnitTestsResult;
4+
import io.github.json031.unittests.DataUnitTests;
5+
import io.github.json031.unittests.RequestUnitTests;
36
import org.junit.jupiter.api.Assertions;
47
import org.springframework.http.*;
5-
import org.springframework.web.client.RestTemplate;
68

7-
import java.time.Duration;
89
import java.util.Map;
910

11+
/**
12+
* This class is used for unit testing api.
13+
*/
1014
public class MCApiTests {
1115

12-
private final RestTemplate restTemplate = new RestTemplate();
13-
1416
/**
1517
* 通用测试方法,验证给定 API 是否能在 timeout 秒内响应
1618
*
@@ -48,54 +50,31 @@ public long assertApiRespondsWithinTimeoutMillis(String url,
4850
Map<String, String> headers,
4951
long timeoutMillis,
5052
boolean verbose) {
51-
try {
52-
HttpHeaders httpHeaders = new HttpHeaders();
53-
if (headers != null) {
54-
headers.forEach(httpHeaders::set);
55-
}
56-
57-
HttpEntity<?> entity;
58-
String finalUrl = url;
59-
60-
if (method == HttpMethod.GET && params != null && !params.isEmpty()) {
61-
// 拼接 GET 参数
62-
StringBuilder queryBuilder = new StringBuilder(url);
63-
queryBuilder.append(url.contains("?") ? "&" : "?");
64-
params.forEach((key, value) -> queryBuilder.append(key).append("=").append(value).append("&"));
65-
finalUrl = queryBuilder.substring(0, queryBuilder.length() - 1); // 去掉最后一个 &
66-
entity = new HttpEntity<>(httpHeaders);
67-
} else {
68-
// POST 请求体(可为 null)
69-
entity = new HttpEntity<>(params, httpHeaders);
70-
}
71-
72-
long start = System.nanoTime();
73-
74-
ResponseEntity<String> response = restTemplate.exchange(
75-
finalUrl,
76-
method,
77-
entity,
78-
String.class
79-
);
80-
81-
long end = System.nanoTime();
82-
long durationMillis = (end - start) / 1_000_000;
83-
84-
if (verbose) {
85-
System.out.println("API Response: " + response.getBody());
86-
System.out.println("Response time: " + durationMillis + " ms");
87-
}
88-
89-
if (durationMillis > timeoutMillis) {
90-
Assertions.fail("API did not respond within " + timeoutMillis + " ms, url: " + finalUrl);
91-
}
92-
93-
return durationMillis;
53+
RequestUnitTestsResult result = RequestUnitTests.requestWitRestTemplate(url, method, params, headers, verbose);
54+
return DataUnitTests.withinTimeOut(result, timeoutMillis);
55+
}
9456

95-
} catch (Exception e) {
96-
Assertions.fail("API call failed for: " + url + " with error: " + e.getMessage());
57+
/**
58+
* 通用测试方法,验证给定 API 是否返回有效json格式数据。
59+
*
60+
* @param url 完整的 API 地址(包括 http/https)
61+
* @param method 请求方式(GET / POST)
62+
* @param params 请求参数(POST body 或 GET 查询参数)
63+
* @param headers 请求头(可选)
64+
* @param verbose 是否打印响应
65+
*/
66+
public void testApiReturnsValidJson(String url,
67+
HttpMethod method,
68+
Map<String, Object> params,
69+
Map<String, String> headers,
70+
boolean verbose) {
71+
RequestUnitTestsResult result = RequestUnitTests.requestWitRestTemplate(url, method, params, headers, verbose);
72+
if (result == null) {
73+
Assertions.fail("API did not respond valid json, url: " + url);
74+
} else {
75+
ResponseEntity<String> response = result.response;
76+
DataUnitTests.isValidJSON(response.getBody());
9777
}
98-
99-
return -1;
10078
}
79+
10180
}

src/main/java/io/github/json031/MCHighConcurrencyTests.java renamed to src/main/java/io/github/json031/apitests/MCHighConcurrencyTests.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
package io.github.json031;
1+
package io.github.json031.apitests;
22

33
import io.github.json031.JavaBean.HighConcurrencyResult;
44
import org.junit.jupiter.api.Assertions;
55
import org.springframework.http.HttpMethod;
6-
import org.springframework.web.client.RestTemplate;
76

87
import java.util.ArrayList;
98
import java.util.List;
109
import java.util.Map;
1110
import java.util.concurrent.*;
1211
import java.util.concurrent.atomic.AtomicInteger;
13-
import java.util.function.Supplier;
1412

13+
/**
14+
* This class is used for unit testing api High Concurrency.
15+
*/
1516
public class MCHighConcurrencyTests {
1617

17-
private final RestTemplate restTemplate = new RestTemplate();
1818
private final MCApiTests mcApiTests = new MCApiTests();
1919

2020
/**
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package io.github.json031.unittests;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import io.github.json031.JavaBean.RequestUnitTestsResult;
5+
import org.junit.jupiter.api.Assertions;
6+
7+
import java.net.URL;
8+
9+
/**
10+
* This class is used for unit testing data.
11+
*/
12+
public class DataUnitTests {
13+
14+
/**
15+
* 验证是否为有效json格式数据。
16+
* @param json 数据
17+
* @return 是否为有效json格式数据。
18+
*/
19+
public static Boolean isValidJSON(String json) {
20+
// 用 Jackson 尝试解析 JSON
21+
ObjectMapper mapper = new ObjectMapper();
22+
try {
23+
mapper.readTree(json); // 验证数据是否为合法JSON格式数据
24+
return true;
25+
} catch (Exception e) {
26+
Assertions.fail("Invalid JSON data: " + json);
27+
return false;
28+
}
29+
}
30+
31+
/**
32+
* 是否超出期望响应时间
33+
* @param result api响应结果
34+
* @param timeoutMillis 期望响应时间
35+
* @return 是否为有效json格式数据。
36+
*/
37+
public static long withinTimeOut(RequestUnitTestsResult result, long timeoutMillis) {
38+
if (result == null) {
39+
return -1;
40+
} else {
41+
long durationMillis = result.durationMillis;
42+
43+
// 耗时是否超过期望值,超过期望值则fail
44+
if (durationMillis > timeoutMillis) {
45+
Assertions.fail("API did not respond within " + timeoutMillis + " ms");
46+
}
47+
48+
return durationMillis;
49+
}
50+
}
51+
52+
/**
53+
* 校验 URL 是否合法
54+
* @param url URL
55+
* @return URL 是否合法
56+
*/
57+
public static Boolean isValidUrl(String url) {
58+
try {
59+
URL parsedUrl = new URL(url);
60+
String protocol = parsedUrl.getProtocol();
61+
Boolean isValidUrl = protocol.equals("http") || protocol.equals("https");
62+
if (!isValidUrl) {
63+
Assertions.fail("Invalid URL: " + url);
64+
}
65+
return isValidUrl;
66+
} catch (Exception e) {
67+
Assertions.fail("Invalid URL: " + url);
68+
return false;
69+
}
70+
}
71+
72+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package io.github.json031.unittests;
2+
3+
import io.github.json031.JavaBean.RequestUnitTestsResult;
4+
import org.junit.jupiter.api.Assertions;
5+
import org.springframework.http.HttpEntity;
6+
import org.springframework.http.HttpHeaders;
7+
import org.springframework.http.HttpMethod;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.web.client.RestTemplate;
10+
11+
import java.util.Map;
12+
13+
/**
14+
* This class is used for unit testing api request.
15+
*/
16+
public class RequestUnitTests {
17+
18+
/**
19+
* 单例实例
20+
*/
21+
private static final RequestUnitTests INSTANCE = new RequestUnitTests();
22+
23+
/**
24+
* 私有 RestTemplate 实例
25+
*/
26+
private final RestTemplate restTemplate;
27+
28+
/**
29+
* 私有构造函数
30+
*/
31+
private RequestUnitTests() {
32+
this.restTemplate = new RestTemplate();
33+
}
34+
35+
/**
36+
* 获取单例
37+
*/
38+
public static RequestUnitTests getInstance() {
39+
return INSTANCE;
40+
}
41+
42+
/**
43+
* 请求 API
44+
*
45+
* @param url 完整的 API 地址(包括 http/https)
46+
* @param method 请求方式(GET / POST)
47+
* @param params 请求参数(POST body 或 GET 查询参数)
48+
* @param headers 请求头(可选)
49+
* @param verbose 是否打印响应
50+
* @return 响应数据
51+
*/
52+
public static RequestUnitTestsResult requestWitRestTemplate(String url,
53+
HttpMethod method,
54+
Map<String, Object> params,
55+
Map<String, String> headers,
56+
boolean verbose) {
57+
//校验 URL 是否合法
58+
if (!DataUnitTests.isValidUrl(url)) {
59+
return null;
60+
}
61+
try {
62+
HttpHeaders httpHeaders = new HttpHeaders();
63+
if (headers != null) {
64+
headers.forEach(httpHeaders::set);
65+
}
66+
67+
// 请求体
68+
HttpEntity<?> entity;
69+
// 请求地址
70+
String finalUrl = url;
71+
72+
if (method == HttpMethod.GET && params != null && !params.isEmpty()) {
73+
// 拼接 GET 参数
74+
StringBuilder queryBuilder = new StringBuilder(url);
75+
queryBuilder.append(url.contains("?") ? "&" : "?");
76+
params.forEach((key, value) -> queryBuilder.append(key).append("=").append(value).append("&"));
77+
finalUrl = queryBuilder.substring(0, queryBuilder.length() - 1); // 去掉最后一个 &
78+
entity = new HttpEntity<>(httpHeaders);
79+
} else {
80+
// POST 请求体(可为 null)
81+
entity = new HttpEntity<>(params, httpHeaders);
82+
}
83+
84+
long start = System.nanoTime();
85+
86+
//校验finalUrl是否合法
87+
if (!DataUnitTests.isValidUrl(finalUrl)) {
88+
return null;
89+
}
90+
ResponseEntity<String> response = INSTANCE.restTemplate.exchange(finalUrl, method, entity, String.class);
91+
92+
long end = System.nanoTime();
93+
// 毫秒级耗时
94+
long durationMillis = (end - start) / 1_000_000;
95+
96+
if (verbose) {
97+
System.out.println("API Response: " + response.getBody());
98+
System.out.println("Response time: " + durationMillis + " ms");
99+
}
100+
101+
RequestUnitTestsResult requestUnitTestsResult = new RequestUnitTestsResult(durationMillis, response);
102+
return requestUnitTestsResult;
103+
104+
} catch (Exception e) {
105+
Assertions.fail("API call failed for: " + url + " with error: " + e.getMessage());
106+
}
107+
108+
return null;
109+
}
110+
}

0 commit comments

Comments
 (0)