ai-engineering-init 1.3.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/hooks/skill-forced-eval.js +4 -1
- package/.claude/settings.json +3 -3
- package/.claude/skills/add-skill/SKILL.md +252 -116
- package/.claude/skills/api-development/SKILL.md +83 -377
- package/.claude/skills/architecture-design/SKILL.md +138 -632
- package/.claude/skills/backend-annotations/SKILL.md +134 -506
- package/.claude/skills/banana-image/SKILL.md +10 -3
- package/.claude/skills/brainstorm/SKILL.md +103 -535
- package/.claude/skills/bug-detective/SKILL.md +147 -1097
- package/.claude/skills/bug-detective/references/error-patterns.md +242 -0
- package/.claude/skills/code-patterns/SKILL.md +116 -426
- package/.claude/skills/code-patterns/references/leniu-code-patterns.md +87 -0
- package/.claude/skills/crud-development/SKILL.md +64 -304
- package/.claude/skills/data-permission/SKILL.md +105 -412
- package/.claude/skills/data-permission/references/custom-data-scope.md +90 -0
- package/.claude/skills/file-oss-management/SKILL.md +106 -714
- package/.claude/skills/file-oss-management/references/entities.md +105 -0
- package/.claude/skills/file-oss-management/references/service-impl.md +104 -0
- package/.claude/skills/leniu-api-development/SKILL.md +142 -626
- package/.claude/skills/leniu-api-development/references/real-examples.md +273 -0
- package/.claude/skills/leniu-architecture-design/SKILL.md +176 -391
- package/.claude/skills/leniu-backend-annotations/SKILL.md +132 -519
- package/.claude/skills/leniu-brainstorm/SKILL.md +132 -541
- package/.claude/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
- package/.claude/skills/leniu-crud-development/SKILL.md +232 -938
- package/.claude/skills/leniu-crud-development/references/templates.md +597 -0
- package/.claude/skills/leniu-customization-location/SKILL.md +410 -0
- package/.claude/skills/leniu-data-permission/SKILL.md +70 -0
- package/.claude/skills/leniu-java-entity/SKILL.md +76 -590
- package/.claude/skills/leniu-java-entity/references/templates.md +237 -0
- package/.claude/skills/leniu-java-export/SKILL.md +94 -379
- package/.claude/skills/leniu-java-logging/SKILL.md +106 -709
- package/.claude/skills/leniu-java-logging/references/data-mask.md +46 -0
- package/.claude/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
- package/.claude/skills/leniu-java-mybatis/SKILL.md +73 -446
- package/.claude/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
- package/.claude/skills/leniu-report-customization/SKILL.md +111 -325
- package/.claude/skills/leniu-report-customization/references/table-fields.md +93 -0
- package/.claude/skills/leniu-report-standard-customization/SKILL.md +328 -0
- package/.claude/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
- package/.claude/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
- package/.claude/skills/leniu-security-guard/SKILL.md +133 -347
- package/.claude/skills/mysql-debug/SKILL.md +364 -0
- package/.claude/skills/openspec-apply-change/SKILL.md +10 -1
- package/.claude/skills/openspec-archive-change/SKILL.md +9 -1
- package/.claude/skills/openspec-bulk-archive-change/SKILL.md +9 -1
- package/.claude/skills/openspec-continue-change/SKILL.md +9 -1
- package/.claude/skills/openspec-explore/SKILL.md +10 -1
- package/.claude/skills/openspec-ff-change/SKILL.md +9 -1
- package/.claude/skills/openspec-new-change/SKILL.md +9 -1
- package/.claude/skills/openspec-onboard/SKILL.md +15 -130
- package/.claude/skills/openspec-sync-specs/SKILL.md +9 -1
- package/.claude/skills/openspec-verify-change/SKILL.md +9 -1
- package/.claude/skills/performance-doctor/SKILL.md +110 -434
- package/.claude/skills/redis-cache/SKILL.md +89 -595
- package/.claude/skills/redis-cache/references/listeners.md +23 -0
- package/.claude/skills/scheduled-jobs/SKILL.md +88 -407
- package/.claude/skills/security-guard/SKILL.md +137 -532
- package/.claude/skills/security-guard/references/encrypt-config.md +103 -0
- package/.claude/skills/security-guard/references/sensitive-strategies.md +42 -0
- package/.claude/skills/sms-mail/SKILL.md +116 -574
- package/.claude/skills/sms-mail/references/mail-config.md +88 -0
- package/.claude/skills/sms-mail/references/sms-config.md +74 -0
- package/.claude/skills/social-login/SKILL.md +112 -514
- package/.claude/skills/social-login/references/provider-configs.md +118 -0
- package/.claude/skills/tenant-management/SKILL.md +129 -444
- package/.claude/skills/tenant-management/references/tenant-scenarios.md +91 -0
- package/.claude/skills/test-development/SKILL.md +86 -540
- package/.claude/skills/test-development/references/parameterized-examples.md +119 -0
- package/.claude/skills/utils-toolkit/SKILL.md +52 -305
- package/.claude/skills/utils-toolkit/references/redis-utils-api.md +56 -0
- package/.claude/skills/websocket-sse/SKILL.md +105 -550
- package/.claude/skills/workflow-engine/SKILL.md +147 -502
- package/.codex/skills/add-skill/SKILL.md +252 -116
- package/.codex/skills/api-development/SKILL.md +172 -599
- package/.codex/skills/architecture-design/SKILL.md +138 -504
- package/.codex/skills/backend-annotations/SKILL.md +134 -496
- package/.codex/skills/banana-image/SKILL.md +10 -3
- package/.codex/skills/brainstorm/SKILL.md +103 -535
- package/.codex/skills/bug-detective/SKILL.md +147 -1097
- package/.codex/skills/bug-detective/references/error-patterns.md +242 -0
- package/.codex/skills/code-patterns/SKILL.md +120 -282
- package/.codex/skills/code-patterns/references/leniu-code-patterns.md +87 -0
- package/.codex/skills/crud-development/SKILL.md +64 -292
- package/.codex/skills/data-permission/SKILL.md +108 -407
- package/.codex/skills/data-permission/references/custom-data-scope.md +90 -0
- package/.codex/skills/database-ops/SKILL.md +8 -154
- package/.codex/skills/error-handler/SKILL.md +10 -0
- package/.codex/skills/file-oss-management/SKILL.md +106 -714
- package/.codex/skills/file-oss-management/references/entities.md +105 -0
- package/.codex/skills/file-oss-management/references/service-impl.md +104 -0
- package/.codex/skills/git-workflow/SKILL.md +27 -5
- package/.codex/skills/leniu-api-development/SKILL.md +142 -626
- package/.codex/skills/leniu-api-development/references/real-examples.md +273 -0
- package/.codex/skills/leniu-architecture-design/SKILL.md +176 -391
- package/.codex/skills/leniu-backend-annotations/SKILL.md +132 -519
- package/.codex/skills/leniu-brainstorm/SKILL.md +132 -541
- package/.codex/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
- package/.codex/skills/leniu-crud-development/SKILL.md +232 -938
- package/.codex/skills/leniu-crud-development/references/templates.md +597 -0
- package/.codex/skills/leniu-customization-location/SKILL.md +410 -0
- package/.codex/skills/leniu-data-permission/SKILL.md +70 -0
- package/.codex/skills/leniu-java-code-style/SKILL.md +510 -0
- package/.codex/skills/leniu-java-entity/SKILL.md +76 -590
- package/.codex/skills/leniu-java-entity/references/templates.md +237 -0
- package/.codex/skills/leniu-java-export/SKILL.md +94 -379
- package/.codex/skills/leniu-java-logging/SKILL.md +106 -709
- package/.codex/skills/leniu-java-logging/references/data-mask.md +46 -0
- package/.codex/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
- package/.codex/skills/leniu-java-mybatis/SKILL.md +73 -446
- package/.codex/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
- package/.codex/skills/leniu-report-customization/SKILL.md +111 -325
- package/.codex/skills/leniu-report-customization/references/table-fields.md +93 -0
- package/.codex/skills/leniu-report-standard-customization/SKILL.md +328 -0
- package/.codex/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
- package/.codex/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
- package/.codex/skills/leniu-security-guard/SKILL.md +133 -347
- package/.codex/skills/mysql-debug/SKILL.md +364 -0
- package/.codex/skills/openspec-apply-change/SKILL.md +10 -1
- package/.codex/skills/openspec-archive-change/SKILL.md +9 -1
- package/.codex/skills/openspec-bulk-archive-change/SKILL.md +9 -1
- package/.codex/skills/openspec-continue-change/SKILL.md +9 -1
- package/.codex/skills/openspec-explore/SKILL.md +10 -1
- package/.codex/skills/openspec-ff-change/SKILL.md +9 -1
- package/.codex/skills/openspec-new-change/SKILL.md +9 -1
- package/.codex/skills/openspec-onboard/SKILL.md +15 -130
- package/.codex/skills/openspec-sync-specs/SKILL.md +9 -1
- package/.codex/skills/openspec-verify-change/SKILL.md +9 -1
- package/.codex/skills/performance-doctor/SKILL.md +110 -434
- package/.codex/skills/project-navigator/SKILL.md +20 -1
- package/.codex/skills/redis-cache/SKILL.md +93 -589
- package/.codex/skills/redis-cache/references/listeners.md +23 -0
- package/.codex/skills/scheduled-jobs/SKILL.md +88 -407
- package/.codex/skills/security-guard/SKILL.md +141 -527
- package/.codex/skills/security-guard/references/encrypt-config.md +103 -0
- package/.codex/skills/security-guard/references/sensitive-strategies.md +42 -0
- package/.codex/skills/sms-mail/SKILL.md +116 -574
- package/.codex/skills/sms-mail/references/mail-config.md +88 -0
- package/.codex/skills/sms-mail/references/sms-config.md +74 -0
- package/.codex/skills/social-login/SKILL.md +112 -514
- package/.codex/skills/social-login/references/provider-configs.md +118 -0
- package/.codex/skills/store-pc/SKILL.md +258 -383
- package/.codex/skills/tenant-management/SKILL.md +129 -444
- package/.codex/skills/tenant-management/references/tenant-scenarios.md +91 -0
- package/.codex/skills/test-development/SKILL.md +86 -540
- package/.codex/skills/test-development/references/parameterized-examples.md +119 -0
- package/.codex/skills/ui-pc/SKILL.md +350 -387
- package/.codex/skills/utils-toolkit/SKILL.md +52 -283
- package/.codex/skills/utils-toolkit/references/redis-utils-api.md +56 -0
- package/.codex/skills/websocket-sse/SKILL.md +105 -550
- package/.codex/skills/workflow-engine/SKILL.md +147 -502
- package/.cursor/hooks.json +3 -3
- package/.cursor/rules/skill-activation.mdc +2 -0
- package/.cursor/skills/add-skill/SKILL.md +252 -116
- package/.cursor/skills/api-development/SKILL.md +83 -377
- package/.cursor/skills/architecture-design/SKILL.md +138 -632
- package/.cursor/skills/backend-annotations/SKILL.md +134 -506
- package/.cursor/skills/banana-image/SKILL.md +10 -3
- package/.cursor/skills/brainstorm/SKILL.md +103 -535
- package/.cursor/skills/bug-detective/SKILL.md +147 -1097
- package/.cursor/skills/bug-detective/references/error-patterns.md +242 -0
- package/.cursor/skills/code-patterns/SKILL.md +116 -426
- package/.cursor/skills/code-patterns/references/leniu-code-patterns.md +87 -0
- package/.cursor/skills/crud-development/SKILL.md +64 -304
- package/.cursor/skills/data-permission/SKILL.md +105 -412
- package/.cursor/skills/data-permission/references/custom-data-scope.md +90 -0
- package/.cursor/skills/file-oss-management/SKILL.md +106 -714
- package/.cursor/skills/file-oss-management/references/entities.md +105 -0
- package/.cursor/skills/file-oss-management/references/service-impl.md +104 -0
- package/.cursor/skills/git-workflow/SKILL.md +27 -5
- package/.cursor/skills/leniu-api-development/SKILL.md +142 -626
- package/.cursor/skills/leniu-api-development/references/real-examples.md +273 -0
- package/.cursor/skills/leniu-architecture-design/SKILL.md +176 -391
- package/.cursor/skills/leniu-backend-annotations/SKILL.md +132 -519
- package/.cursor/skills/leniu-brainstorm/SKILL.md +132 -541
- package/.cursor/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
- package/.cursor/skills/leniu-crud-development/SKILL.md +232 -938
- package/.cursor/skills/leniu-crud-development/references/templates.md +597 -0
- package/.cursor/skills/leniu-customization-location/SKILL.md +410 -0
- package/.cursor/skills/leniu-data-permission/SKILL.md +70 -0
- package/.cursor/skills/leniu-java-code-style/SKILL.md +510 -0
- package/.cursor/skills/leniu-java-entity/SKILL.md +76 -590
- package/.cursor/skills/leniu-java-entity/references/templates.md +237 -0
- package/.cursor/skills/leniu-java-export/SKILL.md +94 -379
- package/.cursor/skills/leniu-java-logging/SKILL.md +106 -709
- package/.cursor/skills/leniu-java-logging/references/data-mask.md +46 -0
- package/.cursor/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
- package/.cursor/skills/leniu-java-mybatis/SKILL.md +73 -446
- package/.cursor/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
- package/.cursor/skills/leniu-report-customization/SKILL.md +111 -325
- package/.cursor/skills/leniu-report-customization/references/table-fields.md +93 -0
- package/.cursor/skills/leniu-report-standard-customization/SKILL.md +328 -0
- package/.cursor/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
- package/.cursor/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
- package/.cursor/skills/leniu-security-guard/SKILL.md +133 -347
- package/.cursor/skills/mysql-debug/SKILL.md +364 -0
- package/.cursor/skills/openspec-apply-change/SKILL.md +10 -1
- package/.cursor/skills/openspec-archive-change/SKILL.md +9 -1
- package/.cursor/skills/openspec-bulk-archive-change/SKILL.md +9 -1
- package/.cursor/skills/openspec-continue-change/SKILL.md +9 -1
- package/.cursor/skills/openspec-explore/SKILL.md +10 -1
- package/.cursor/skills/openspec-ff-change/SKILL.md +9 -1
- package/.cursor/skills/openspec-new-change/SKILL.md +9 -1
- package/.cursor/skills/openspec-onboard/SKILL.md +15 -130
- package/.cursor/skills/openspec-sync-specs/SKILL.md +9 -1
- package/.cursor/skills/openspec-verify-change/SKILL.md +9 -1
- package/.cursor/skills/performance-doctor/SKILL.md +110 -434
- package/.cursor/skills/redis-cache/SKILL.md +89 -595
- package/.cursor/skills/redis-cache/references/listeners.md +23 -0
- package/.cursor/skills/scheduled-jobs/SKILL.md +88 -407
- package/.cursor/skills/security-guard/SKILL.md +137 -532
- package/.cursor/skills/security-guard/references/encrypt-config.md +103 -0
- package/.cursor/skills/security-guard/references/sensitive-strategies.md +42 -0
- package/.cursor/skills/sms-mail/SKILL.md +116 -574
- package/.cursor/skills/sms-mail/references/mail-config.md +88 -0
- package/.cursor/skills/sms-mail/references/sms-config.md +74 -0
- package/.cursor/skills/social-login/SKILL.md +112 -514
- package/.cursor/skills/social-login/references/provider-configs.md +118 -0
- package/.cursor/skills/tenant-management/SKILL.md +129 -444
- package/.cursor/skills/tenant-management/references/tenant-scenarios.md +91 -0
- package/.cursor/skills/test-development/SKILL.md +86 -540
- package/.cursor/skills/test-development/references/parameterized-examples.md +119 -0
- package/.cursor/skills/utils-toolkit/SKILL.md +52 -305
- package/.cursor/skills/utils-toolkit/references/redis-utils-api.md +56 -0
- package/.cursor/skills/websocket-sse/SKILL.md +105 -550
- package/.cursor/skills/workflow-engine/SKILL.md +147 -502
- package/AGENTS.md +1 -0
- package/package.json +1 -1
|
@@ -18,45 +18,25 @@ description: |
|
|
|
18
18
|
|
|
19
19
|
# 测试开发规范
|
|
20
20
|
|
|
21
|
-
> **核心原则**:本项目使用标准的 JUnit5 + Spring Boot Test,根据测试场景选择是否启动 Spring 容器!
|
|
22
|
-
|
|
23
21
|
## 测试分层策略
|
|
24
22
|
|
|
25
|
-
| 层次 | 测试类型 |
|
|
26
|
-
|
|
27
|
-
|
|
|
28
|
-
|
|
|
23
|
+
| 层次 | 测试类型 | 启动 Spring | 执行速度 |
|
|
24
|
+
|------|---------|------------|---------|
|
|
25
|
+
| 单元测试 | 工具类/枚举/POJO | 否 | < 1s |
|
|
26
|
+
| 集成测试 | Service/Controller/Mapper | 是(`@SpringBootTest`) | 5-10s |
|
|
29
27
|
|
|
30
28
|
## 测试文件位置
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
| 测试资源 | `src/test/resources` | 测试配置文件、测试数据等 |
|
|
36
|
-
|
|
37
|
-
## 核心依赖
|
|
38
|
-
|
|
39
|
-
Spring Boot Test 已包含在 `spring-boot-starter-test` 中,包含:
|
|
40
|
-
- **JUnit 5**:测试框架
|
|
41
|
-
- **Mockito**:Mock 框架
|
|
42
|
-
- **Spring Test**:Spring 测试支持
|
|
43
|
-
|
|
44
|
-
```xml
|
|
45
|
-
<!-- 在业务模块的 pom.xml 中已包含 -->
|
|
46
|
-
<dependency>
|
|
47
|
-
<groupId>org.springframework.boot</groupId>
|
|
48
|
-
<artifactId>spring-boot-starter-test</artifactId>
|
|
49
|
-
<scope>test</scope>
|
|
50
|
-
</dependency>
|
|
30
|
+
```
|
|
31
|
+
src/test/java/org/dromara/test/ # 通用测试
|
|
32
|
+
src/test/java/org/dromara/{模块}/ # 模块测试(与源码包路径一致)
|
|
51
33
|
```
|
|
52
34
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
## 1. 单元测试(纯 JUnit5)
|
|
35
|
+
**重要**:`@SpringBootTest` 只能在 SpringBoot 主包下使用(需包含 main 方法和 yml 配置)。
|
|
56
36
|
|
|
57
|
-
|
|
37
|
+
---
|
|
58
38
|
|
|
59
|
-
|
|
39
|
+
## 1. 单元测试(纯 JUnit5,无 Spring)
|
|
60
40
|
|
|
61
41
|
```java
|
|
62
42
|
package org.dromara.test;
|
|
@@ -65,164 +45,35 @@ import org.junit.jupiter.api.Assertions;
|
|
|
65
45
|
import org.junit.jupiter.api.DisplayName;
|
|
66
46
|
import org.junit.jupiter.api.Test;
|
|
67
47
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
* 参考:ruoyi-admin/src/test/java/org/dromara/test/AssertUnitTest.java
|
|
71
|
-
*/
|
|
72
|
-
@DisplayName("断言单元测试案例")
|
|
73
|
-
public class AssertUnitTest {
|
|
74
|
-
|
|
75
|
-
@Test
|
|
76
|
-
@DisplayName("测试 assertEquals 方法")
|
|
77
|
-
public void testAssertEquals() {
|
|
78
|
-
Assertions.assertEquals("666", new String("666"));
|
|
79
|
-
Assertions.assertNotEquals("777", new String("666"));
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
@Test
|
|
83
|
-
@DisplayName("测试 assertTrue 方法")
|
|
84
|
-
public void testAssertTrue() {
|
|
85
|
-
Assertions.assertTrue(true);
|
|
86
|
-
Assertions.assertFalse(false);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
@Test
|
|
90
|
-
@DisplayName("测试 assertNull 方法")
|
|
91
|
-
public void testAssertNull() {
|
|
92
|
-
Assertions.assertNull(null);
|
|
93
|
-
Assertions.assertNotNull("not null");
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
**常用断言方法:**
|
|
99
|
-
|
|
100
|
-
```java
|
|
101
|
-
// 相等性断言
|
|
102
|
-
Assertions.assertEquals(expected, actual);
|
|
103
|
-
Assertions.assertNotEquals(expected, actual);
|
|
104
|
-
|
|
105
|
-
// 同一性断言
|
|
106
|
-
Assertions.assertSame(obj1, obj2);
|
|
107
|
-
Assertions.assertNotSame(obj1, obj2);
|
|
108
|
-
|
|
109
|
-
// 布尔断言
|
|
110
|
-
Assertions.assertTrue(condition);
|
|
111
|
-
Assertions.assertFalse(condition);
|
|
112
|
-
|
|
113
|
-
// 空值断言
|
|
114
|
-
Assertions.assertNull(object);
|
|
115
|
-
Assertions.assertNotNull(object);
|
|
116
|
-
|
|
117
|
-
// 异常断言
|
|
118
|
-
Assertions.assertThrows(Exception.class, () -> {
|
|
119
|
-
// 会抛出异常的代码
|
|
120
|
-
});
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
## 2. Spring 集成测试(@SpringBootTest)
|
|
126
|
-
|
|
127
|
-
**适用场景:** 需要 Spring 容器的测试(Service、Controller、Mapper、需要依赖注入的场景)
|
|
128
|
-
|
|
129
|
-
**特点:** 使用 `@SpringBootTest` 启动完整的 Spring 容器,可以使用 `@Autowired` 注入 Bean。
|
|
130
|
-
|
|
131
|
-
**重要提示:** `@SpringBootTest` 只能在 SpringBoot 主包下使用,需包含 main 方法与 yml 配置文件。
|
|
132
|
-
|
|
133
|
-
```java
|
|
134
|
-
package org.dromara.test;
|
|
135
|
-
|
|
136
|
-
import org.dromara.common.web.config.properties.CaptchaProperties;
|
|
137
|
-
import org.junit.jupiter.api.*;
|
|
138
|
-
import org.springframework.beans.factory.annotation.Autowired;
|
|
139
|
-
import org.springframework.boot.test.context.SpringBootTest;
|
|
140
|
-
|
|
141
|
-
import java.util.concurrent.TimeUnit;
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Spring 集成测试案例
|
|
145
|
-
* 参考:ruoyi-admin/src/test/java/org/dromara/test/DemoUnitTest.java
|
|
146
|
-
*/
|
|
147
|
-
@SpringBootTest
|
|
148
|
-
@DisplayName("单元测试案例")
|
|
149
|
-
public class DemoUnitTest {
|
|
150
|
-
|
|
151
|
-
@Autowired
|
|
152
|
-
private CaptchaProperties captchaProperties;
|
|
153
|
-
|
|
154
|
-
@Test
|
|
155
|
-
@DisplayName("测试 @SpringBootTest @Test @DisplayName 注解")
|
|
156
|
-
public void testTest() {
|
|
157
|
-
System.out.println(captchaProperties);
|
|
158
|
-
Assertions.assertNotNull(captchaProperties);
|
|
159
|
-
}
|
|
48
|
+
@DisplayName("StringUtils 测试")
|
|
49
|
+
public class StringUtilsTest {
|
|
160
50
|
|
|
161
51
|
@Test
|
|
162
|
-
@DisplayName("测试
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
52
|
+
@DisplayName("测试 isBlank 方法")
|
|
53
|
+
public void testIsBlank() {
|
|
54
|
+
Assertions.assertTrue(StringUtils.isBlank(null));
|
|
55
|
+
Assertions.assertTrue(StringUtils.isBlank(""));
|
|
56
|
+
Assertions.assertFalse(StringUtils.isBlank("hello"));
|
|
166
57
|
}
|
|
167
58
|
|
|
168
59
|
@Test
|
|
169
|
-
@DisplayName("
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
@RepeatedTest(3)
|
|
176
|
-
@DisplayName("测试 @RepeatedTest 注解")
|
|
177
|
-
public void testRepeatedTest() {
|
|
178
|
-
System.out.println("重复测试");
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
@BeforeAll
|
|
182
|
-
public static void testBeforeAll() {
|
|
183
|
-
System.out.println("@BeforeAll - 所有测试前执行一次");
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
@BeforeEach
|
|
187
|
-
public void testBeforeEach() {
|
|
188
|
-
System.out.println("@BeforeEach - 每个测试前执行");
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
@AfterEach
|
|
192
|
-
public void testAfterEach() {
|
|
193
|
-
System.out.println("@AfterEach - 每个测试后执行");
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
@AfterAll
|
|
197
|
-
public static void testAfterAll() {
|
|
198
|
-
System.out.println("@AfterAll - 所有测试后执行一次");
|
|
60
|
+
@DisplayName("测试异常抛出")
|
|
61
|
+
public void testThrowsException() {
|
|
62
|
+
Assertions.assertThrows(NullPointerException.class, () -> {
|
|
63
|
+
String str = null;
|
|
64
|
+
str.length();
|
|
65
|
+
});
|
|
199
66
|
}
|
|
200
67
|
}
|
|
201
68
|
```
|
|
202
69
|
|
|
203
70
|
---
|
|
204
71
|
|
|
205
|
-
##
|
|
206
|
-
|
|
207
|
-
**适用场景:** Service 层业务逻辑测试,需要数据库操作
|
|
208
|
-
|
|
209
|
-
**特点:** 使用 `@SpringBootTest` + `@Transactional`,测试后自动回滚,不污染数据库。
|
|
72
|
+
## 2. Service 集成测试
|
|
210
73
|
|
|
211
74
|
```java
|
|
212
|
-
package org.dromara.demo.service;
|
|
213
|
-
|
|
214
|
-
import org.dromara.demo.domain.bo.TestDemoBo;
|
|
215
|
-
import org.dromara.demo.domain.vo.TestDemoVo;
|
|
216
|
-
import org.junit.jupiter.api.*;
|
|
217
|
-
import org.springframework.beans.factory.annotation.Autowired;
|
|
218
|
-
import org.springframework.boot.test.context.SpringBootTest;
|
|
219
|
-
import org.springframework.transaction.annotation.Transactional;
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Service 层测试示例
|
|
223
|
-
*/
|
|
224
75
|
@SpringBootTest
|
|
225
|
-
@Transactional //
|
|
76
|
+
@Transactional // 测试后自动回滚,不污染数据库
|
|
226
77
|
@DisplayName("TestDemo Service 测试")
|
|
227
78
|
public class TestDemoServiceTest {
|
|
228
79
|
|
|
@@ -232,66 +83,36 @@ public class TestDemoServiceTest {
|
|
|
232
83
|
@Test
|
|
233
84
|
@DisplayName("测试添加数据")
|
|
234
85
|
public void testAdd() {
|
|
235
|
-
// Arrange
|
|
86
|
+
// Arrange
|
|
236
87
|
TestDemoBo bo = new TestDemoBo();
|
|
237
88
|
bo.setDeptId(103L);
|
|
238
89
|
bo.setUserId(1L);
|
|
239
|
-
bo.setOrderNum(1);
|
|
240
90
|
bo.setTestKey("测试数据");
|
|
241
91
|
bo.setValue("test_value");
|
|
242
92
|
|
|
243
|
-
// Act
|
|
93
|
+
// Act
|
|
244
94
|
Boolean result = testDemoService.insertByBo(bo);
|
|
245
95
|
|
|
246
|
-
// Assert
|
|
96
|
+
// Assert
|
|
247
97
|
Assertions.assertTrue(result);
|
|
248
98
|
}
|
|
249
99
|
|
|
250
100
|
@Test
|
|
251
101
|
@DisplayName("测试查询详情")
|
|
252
102
|
public void testGetById() {
|
|
253
|
-
// 先添加测试数据
|
|
254
103
|
TestDemoBo bo = new TestDemoBo();
|
|
255
|
-
bo.setDeptId(103L);
|
|
256
|
-
bo.setUserId(1L);
|
|
257
|
-
bo.setOrderNum(2);
|
|
258
104
|
bo.setTestKey("测试查询");
|
|
259
105
|
bo.setValue("test_query");
|
|
260
106
|
testDemoService.insertByBo(bo);
|
|
261
107
|
|
|
262
|
-
// 查询详情
|
|
263
108
|
TestDemoVo vo = testDemoService.queryById(bo.getId());
|
|
264
|
-
|
|
265
|
-
// 断言
|
|
266
109
|
Assertions.assertNotNull(vo);
|
|
267
110
|
Assertions.assertEquals("测试查询", vo.getTestKey());
|
|
268
111
|
}
|
|
269
|
-
|
|
270
|
-
@Test
|
|
271
|
-
@DisplayName("测试更新数据")
|
|
272
|
-
public void testUpdate() {
|
|
273
|
-
// 先添加测试数据
|
|
274
|
-
TestDemoBo bo = new TestDemoBo();
|
|
275
|
-
bo.setDeptId(103L);
|
|
276
|
-
bo.setUserId(1L);
|
|
277
|
-
bo.setOrderNum(3);
|
|
278
|
-
bo.setTestKey("原始名称");
|
|
279
|
-
bo.setValue("original");
|
|
280
|
-
testDemoService.insertByBo(bo);
|
|
281
|
-
|
|
282
|
-
// 更新数据
|
|
283
|
-
bo.setTestKey("更新后名称");
|
|
284
|
-
Boolean result = testDemoService.updateByBo(bo);
|
|
285
|
-
|
|
286
|
-
// 验证
|
|
287
|
-
Assertions.assertTrue(result);
|
|
288
|
-
TestDemoVo vo = testDemoService.queryById(bo.getId());
|
|
289
|
-
Assertions.assertEquals("更新后名称", vo.getTestKey());
|
|
290
|
-
}
|
|
291
112
|
}
|
|
292
113
|
```
|
|
293
114
|
|
|
294
|
-
|
|
115
|
+
### Mock 外部依赖
|
|
295
116
|
|
|
296
117
|
```java
|
|
297
118
|
@SpringBootTest
|
|
@@ -301,21 +122,17 @@ public class OrderServiceTest {
|
|
|
301
122
|
@Autowired
|
|
302
123
|
private IOrderService orderService;
|
|
303
124
|
|
|
304
|
-
@MockBean
|
|
125
|
+
@MockBean
|
|
305
126
|
private IPaymentService paymentService;
|
|
306
127
|
|
|
307
128
|
@Test
|
|
308
129
|
@DisplayName("测试订单支付(Mock 支付服务)")
|
|
309
130
|
public void testPayOrder() {
|
|
310
131
|
Long orderId = 123L;
|
|
311
|
-
|
|
312
|
-
// Mock 行为
|
|
313
132
|
Mockito.when(paymentService.pay(orderId)).thenReturn(true);
|
|
314
133
|
|
|
315
|
-
// 执行
|
|
316
134
|
Boolean success = orderService.payOrder(orderId);
|
|
317
135
|
|
|
318
|
-
// 断言
|
|
319
136
|
Assertions.assertTrue(success);
|
|
320
137
|
Mockito.verify(paymentService, Mockito.times(1)).pay(orderId);
|
|
321
138
|
}
|
|
@@ -324,28 +141,9 @@ public class OrderServiceTest {
|
|
|
324
141
|
|
|
325
142
|
---
|
|
326
143
|
|
|
327
|
-
##
|
|
328
|
-
|
|
329
|
-
**适用场景:** HTTP 接口测试,完整请求链路
|
|
330
|
-
|
|
331
|
-
**特点:** 使用 `@SpringBootTest` + `@AutoConfigureMockMvc`,模拟 HTTP 请求。
|
|
144
|
+
## 3. Controller 测试
|
|
332
145
|
|
|
333
146
|
```java
|
|
334
|
-
package org.dromara.demo.controller;
|
|
335
|
-
|
|
336
|
-
import org.junit.jupiter.api.*;
|
|
337
|
-
import org.springframework.beans.factory.annotation.Autowired;
|
|
338
|
-
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
|
339
|
-
import org.springframework.boot.test.context.SpringBootTest;
|
|
340
|
-
import org.springframework.http.MediaType;
|
|
341
|
-
import org.springframework.test.web.servlet.MockMvc;
|
|
342
|
-
|
|
343
|
-
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
|
344
|
-
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Controller 层测试示例
|
|
348
|
-
*/
|
|
349
147
|
@SpringBootTest
|
|
350
148
|
@AutoConfigureMockMvc
|
|
351
149
|
@DisplayName("TestDemo Controller 测试")
|
|
@@ -364,14 +162,6 @@ public class TestDemoControllerTest {
|
|
|
364
162
|
.andExpect(jsonPath("$.code").value(200));
|
|
365
163
|
}
|
|
366
164
|
|
|
367
|
-
@Test
|
|
368
|
-
@DisplayName("测试查询详情")
|
|
369
|
-
public void testGetById() throws Exception {
|
|
370
|
-
mockMvc.perform(get("/demo/demo/1"))
|
|
371
|
-
.andExpect(status().isOk())
|
|
372
|
-
.andExpect(jsonPath("$.code").value(200));
|
|
373
|
-
}
|
|
374
|
-
|
|
375
165
|
@Test
|
|
376
166
|
@DisplayName("测试添加数据")
|
|
377
167
|
public void testAdd() throws Exception {
|
|
@@ -379,7 +169,6 @@ public class TestDemoControllerTest {
|
|
|
379
169
|
{
|
|
380
170
|
"deptId": 103,
|
|
381
171
|
"userId": 1,
|
|
382
|
-
"orderNum": 1,
|
|
383
172
|
"testKey": "测试数据",
|
|
384
173
|
"value": "test_value"
|
|
385
174
|
}
|
|
@@ -391,365 +180,122 @@ public class TestDemoControllerTest {
|
|
|
391
180
|
.andExpect(status().isOk())
|
|
392
181
|
.andExpect(jsonPath("$.code").value(200));
|
|
393
182
|
}
|
|
394
|
-
|
|
395
|
-
@Test
|
|
396
|
-
@DisplayName("测试更新数据")
|
|
397
|
-
public void testUpdate() throws Exception {
|
|
398
|
-
String requestBody = """
|
|
399
|
-
{
|
|
400
|
-
"id": 1,
|
|
401
|
-
"deptId": 103,
|
|
402
|
-
"userId": 1,
|
|
403
|
-
"orderNum": 1,
|
|
404
|
-
"testKey": "更新后数据",
|
|
405
|
-
"value": "updated_value"
|
|
406
|
-
}
|
|
407
|
-
""";
|
|
408
|
-
|
|
409
|
-
mockMvc.perform(put("/demo/demo")
|
|
410
|
-
.contentType(MediaType.APPLICATION_JSON)
|
|
411
|
-
.content(requestBody))
|
|
412
|
-
.andExpect(status().isOk())
|
|
413
|
-
.andExpect(jsonPath("$.code").value(200));
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
@Test
|
|
417
|
-
@DisplayName("测试删除数据")
|
|
418
|
-
public void testDelete() throws Exception {
|
|
419
|
-
mockMvc.perform(delete("/demo/demo/1,2,3"))
|
|
420
|
-
.andExpect(status().isOk())
|
|
421
|
-
.andExpect(jsonPath("$.code").value(200));
|
|
422
|
-
}
|
|
423
183
|
}
|
|
424
184
|
```
|
|
425
185
|
|
|
426
186
|
---
|
|
427
187
|
|
|
428
|
-
##
|
|
429
|
-
|
|
430
|
-
**适用场景:** 需要用多组数据测试同一个方法
|
|
431
|
-
|
|
432
|
-
**特点:** 使用 `@ParameterizedTest` 替代 `@Test`,配合数据源注解提供测试数据。
|
|
188
|
+
## 4. 参数化测试
|
|
433
189
|
|
|
434
190
|
```java
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
import org.dromara.common.core.enums.UserType;
|
|
438
|
-
import org.junit.jupiter.api.*;
|
|
439
|
-
import org.junit.jupiter.params.ParameterizedTest;
|
|
440
|
-
import org.junit.jupiter.params.provider.*;
|
|
441
|
-
|
|
442
|
-
import java.util.ArrayList;
|
|
443
|
-
import java.util.List;
|
|
444
|
-
import java.util.stream.Stream;
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* 参数化测试案例
|
|
448
|
-
* 参考:ruoyi-admin/src/test/java/org/dromara/test/ParamUnitTest.java
|
|
449
|
-
*/
|
|
450
|
-
@DisplayName("带参数单元测试案例")
|
|
191
|
+
@DisplayName("参数化测试")
|
|
451
192
|
public class ParamUnitTest {
|
|
452
193
|
|
|
453
194
|
@ParameterizedTest
|
|
454
|
-
@DisplayName("测试 @ValueSource 注解")
|
|
455
195
|
@ValueSource(strings = {"t1", "t2", "t3"})
|
|
456
196
|
public void testValueSource(String str) {
|
|
457
|
-
System.out.println(str);
|
|
458
197
|
Assertions.assertNotNull(str);
|
|
459
198
|
}
|
|
460
199
|
|
|
461
200
|
@ParameterizedTest
|
|
462
|
-
@DisplayName("测试 @NullSource 注解")
|
|
463
|
-
@NullSource
|
|
464
|
-
public void testNullSource(String str) {
|
|
465
|
-
System.out.println(str);
|
|
466
|
-
Assertions.assertNull(str);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
@ParameterizedTest
|
|
470
|
-
@DisplayName("测试 @EnumSource 注解")
|
|
471
201
|
@EnumSource(UserType.class)
|
|
472
202
|
public void testEnumSource(UserType type) {
|
|
473
|
-
System.out.println(type.getUserType());
|
|
474
203
|
Assertions.assertNotNull(type);
|
|
475
204
|
}
|
|
476
205
|
|
|
477
206
|
@ParameterizedTest
|
|
478
|
-
@
|
|
207
|
+
@CsvSource({"1, Banner, 横幅广告", "2, Popup, 弹窗广告"})
|
|
208
|
+
public void testCsvData(String code, String name, String desc) {
|
|
209
|
+
Assertions.assertNotNull(code);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@ParameterizedTest
|
|
479
213
|
@MethodSource("getParam")
|
|
480
214
|
public void testMethodSource(String str) {
|
|
481
|
-
System.out.println(str);
|
|
482
215
|
Assertions.assertNotNull(str);
|
|
483
216
|
}
|
|
484
217
|
|
|
485
218
|
public static Stream<String> getParam() {
|
|
486
|
-
|
|
487
|
-
list.add("t1");
|
|
488
|
-
list.add("t2");
|
|
489
|
-
list.add("t3");
|
|
490
|
-
return list.stream();
|
|
219
|
+
return Stream.of("t1", "t2", "t3");
|
|
491
220
|
}
|
|
492
|
-
|
|
493
|
-
@BeforeEach
|
|
494
|
-
public void testBeforeEach() {
|
|
495
|
-
System.out.println("@BeforeEach - 每个测试前执行");
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
@AfterEach
|
|
499
|
-
public void testAfterEach() {
|
|
500
|
-
System.out.println("@AfterEach - 每个测试后执行");
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
**更多参数化测试示例:**
|
|
506
|
-
|
|
507
|
-
```java
|
|
508
|
-
// CSV 数据源
|
|
509
|
-
@ParameterizedTest
|
|
510
|
-
@DisplayName("测试 CSV 数据")
|
|
511
|
-
@CsvSource({
|
|
512
|
-
"1, Banner, 横幅广告",
|
|
513
|
-
"2, Popup, 弹窗广告"
|
|
514
|
-
})
|
|
515
|
-
public void testCsvData(String code, String name, String desc) {
|
|
516
|
-
Assertions.assertNotNull(code);
|
|
517
|
-
Assertions.assertNotNull(name);
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// 多个值源
|
|
521
|
-
@ParameterizedTest
|
|
522
|
-
@DisplayName("测试多个整数")
|
|
523
|
-
@ValueSource(ints = {1, 2, 3, 4, 5})
|
|
524
|
-
public void testIntValues(int num) {
|
|
525
|
-
Assertions.assertTrue(num > 0);
|
|
526
221
|
}
|
|
527
|
-
|
|
528
|
-
// 空值和空字符串
|
|
529
|
-
@ParameterizedTest
|
|
530
|
-
@DisplayName("测试空值")
|
|
531
|
-
@NullAndEmptySource
|
|
532
|
-
@ValueSource(strings = {" ", "\t", "\n"})
|
|
533
|
-
public void testBlankStrings(String input) {
|
|
534
|
-
Assertions.assertTrue(input == null || input.isBlank());
|
|
535
|
-
}
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
---
|
|
539
|
-
|
|
540
|
-
## 6. 测试标签和分组(@Tag)
|
|
541
|
-
|
|
542
|
-
**适用场景:** 需要对测试进行分组,选择性运行某些测试
|
|
543
|
-
|
|
544
|
-
**特点:** 使用 `@Tag` 注解标记测试,可以按标签过滤执行。
|
|
545
|
-
|
|
546
|
-
```java
|
|
547
|
-
package org.dromara.test;
|
|
548
|
-
|
|
549
|
-
import org.junit.jupiter.api.*;
|
|
550
|
-
import org.springframework.boot.test.context.SpringBootTest;
|
|
551
|
-
|
|
552
|
-
/**
|
|
553
|
-
* 标签单元测试案例
|
|
554
|
-
* 参考:ruoyi-admin/src/test/java/org/dromara/test/TagUnitTest.java
|
|
555
|
-
*/
|
|
556
|
-
@SpringBootTest
|
|
557
|
-
@DisplayName("标签单元测试案例")
|
|
558
|
-
public class TagUnitTest {
|
|
559
|
-
|
|
560
|
-
@Test
|
|
561
|
-
@Tag("dev")
|
|
562
|
-
@DisplayName("测试 @Tag dev")
|
|
563
|
-
public void testTagDev() {
|
|
564
|
-
System.out.println("dev 环境测试");
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
@Test
|
|
568
|
-
@Tag("prod")
|
|
569
|
-
@DisplayName("测试 @Tag prod")
|
|
570
|
-
public void testTagProd() {
|
|
571
|
-
System.out.println("prod 环境测试");
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
@Test
|
|
575
|
-
@Tag("local")
|
|
576
|
-
@DisplayName("测试 @Tag local")
|
|
577
|
-
public void testTagLocal() {
|
|
578
|
-
System.out.println("local 环境测试");
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
@Test
|
|
582
|
-
@Tag("exclude")
|
|
583
|
-
@DisplayName("测试 @Tag exclude")
|
|
584
|
-
public void testTagExclude() {
|
|
585
|
-
System.out.println("排除的测试");
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
@BeforeEach
|
|
589
|
-
public void testBeforeEach() {
|
|
590
|
-
System.out.println("@BeforeEach - 每个测试前执行");
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
@AfterEach
|
|
594
|
-
public void testAfterEach() {
|
|
595
|
-
System.out.println("@AfterEach - 每个测试后执行");
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
```
|
|
599
|
-
|
|
600
|
-
**运行指定标签的测试:**
|
|
601
|
-
|
|
602
|
-
```bash
|
|
603
|
-
# Maven 运行指定标签的测试
|
|
604
|
-
mvn test -Dgroups=dev
|
|
605
|
-
|
|
606
|
-
# Maven 排除指定标签的测试
|
|
607
|
-
mvn test -DexcludedGroups=exclude
|
|
608
|
-
|
|
609
|
-
# IDEA 中配置标签过滤
|
|
610
|
-
# Run → Edit Configurations → Test kind: Tags → Tag expression: dev
|
|
611
222
|
```
|
|
612
223
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
## 开发检查清单
|
|
616
|
-
|
|
617
|
-
### 测试文件规范
|
|
618
|
-
|
|
619
|
-
- [ ] **测试类命名**:`{被测试类名}Test`(如 `StringUtilsTest`、`DemoUnitTest`)
|
|
620
|
-
- [ ] **测试类位置**:`src/test/java` 目录下,包路径与源码一致
|
|
621
|
-
- [ ] **测试方法命名**:`test{功能}`(如 `testIsBlank`、`testAdd`)
|
|
622
|
-
- [ ] **使用 @DisplayName**:为测试类和方法添加中文描述
|
|
623
|
-
|
|
624
|
-
### 注解选择
|
|
625
|
-
|
|
626
|
-
- [ ] **纯单元测试**:不使用 `@SpringBootTest`,适合工具类/枚举/POJO
|
|
627
|
-
- [ ] **集成测试**:使用 `@SpringBootTest`,适合 Service/Controller/Mapper
|
|
628
|
-
- [ ] **Service 测试**:使用 `@SpringBootTest` + `@Transactional`,测试后自动回滚
|
|
629
|
-
- [ ] **Controller 测试**:使用 `@SpringBootTest` + `@AutoConfigureMockMvc`
|
|
630
|
-
|
|
631
|
-
### 断言规范
|
|
632
|
-
|
|
633
|
-
- [ ] **使用 JUnit5 Assertions**:`Assertions.assertEquals()` / `Assertions.assertTrue()` 等
|
|
634
|
-
- [ ] **异常断言**:`Assertions.assertThrows(Exception.class, () -> {...})`
|
|
635
|
-
- [ ] **空值断言**:`Assertions.assertNull()` / `Assertions.assertNotNull()`
|
|
636
|
-
|
|
637
|
-
### Mock 规范
|
|
638
|
-
|
|
639
|
-
- [ ] **Spring 测试**:使用 `@MockBean` Mock Spring Bean
|
|
640
|
-
- [ ] **单元测试**:使用 `@Mock` / `@InjectMocks`(Mockito)
|
|
641
|
-
- [ ] **验证调用**:使用 `Mockito.verify()` 验证 Mock 方法被调用
|
|
224
|
+
> 更多参数化测试示例详见 `references/parameterized-examples.md`
|
|
642
225
|
|
|
643
226
|
---
|
|
644
227
|
|
|
645
|
-
##
|
|
646
|
-
|
|
647
|
-
| 错误写法 | 正确写法 | 原因 |
|
|
648
|
-
|---------|---------|------|
|
|
649
|
-
| 测试类在 `src/main/java` | 测试类在 `src/test/java` | 测试代码不应打包到生产环境 |
|
|
650
|
-
| `@Test public void test()` | `@Test @DisplayName("xxx") public void testXxx()` | 缺少描述和命名规范 |
|
|
651
|
-
| 忘记添加 `@SpringBootTest` | 需要依赖注入时添加 `@SpringBootTest` | 无法注入 Spring Bean |
|
|
652
|
-
| `@SpringBootTest` 在非主包下 | 测试类必须在主包下(包含 main 方法) | `@SpringBootTest` 需要找到主类 |
|
|
653
|
-
| Mock 后不验证调用 | `Mockito.verify(mockObj).method()` | Mock 应验证调用次数和参数 |
|
|
654
|
-
| 测试方法相互依赖 | 每个测试方法独立 | 测试应独立可并行执行 |
|
|
655
|
-
| Service 测试不加 `@Transactional` | 使用 `@Transactional` 自动回滚 | 避免污染数据库 |
|
|
656
|
-
| 硬编码测试数据 | 使用变量或常量 | 提高可维护性 |
|
|
657
|
-
|
|
658
|
-
---
|
|
659
|
-
|
|
660
|
-
## 异常测试
|
|
661
|
-
|
|
662
|
-
**适用场景:** 测试方法是否正确抛出异常
|
|
228
|
+
## 5. 异常测试
|
|
663
229
|
|
|
664
230
|
```java
|
|
665
|
-
import org.dromara.common.core.exception.ServiceException;
|
|
666
|
-
import org.junit.jupiter.api.Assertions;
|
|
667
|
-
import org.junit.jupiter.api.DisplayName;
|
|
668
|
-
import org.junit.jupiter.api.Test;
|
|
669
|
-
|
|
670
231
|
@Test
|
|
671
|
-
@DisplayName("
|
|
232
|
+
@DisplayName("测试 key 为空抛出异常")
|
|
672
233
|
public void testAdd_ThrowsException() {
|
|
673
234
|
TestDemoBo bo = new TestDemoBo();
|
|
674
|
-
//
|
|
235
|
+
// 不设置必填字段
|
|
675
236
|
|
|
676
|
-
// 断言抛出 ServiceException 异常
|
|
677
237
|
ServiceException exception = Assertions.assertThrows(
|
|
678
238
|
ServiceException.class,
|
|
679
239
|
() -> testDemoService.insertByBo(bo)
|
|
680
240
|
);
|
|
681
|
-
|
|
682
|
-
// 验证异常消息
|
|
683
241
|
Assertions.assertTrue(exception.getMessage().contains("key键不能为空"));
|
|
684
242
|
}
|
|
685
|
-
|
|
686
|
-
@Test
|
|
687
|
-
@DisplayName("测试空指针异常")
|
|
688
|
-
public void testNullPointerException() {
|
|
689
|
-
Assertions.assertThrows(
|
|
690
|
-
NullPointerException.class,
|
|
691
|
-
() -> {
|
|
692
|
-
String str = null;
|
|
693
|
-
str.length();
|
|
694
|
-
}
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
243
|
```
|
|
698
244
|
|
|
699
245
|
---
|
|
700
246
|
|
|
701
|
-
##
|
|
247
|
+
## 6. 测试标签(@Tag)
|
|
702
248
|
|
|
703
|
-
```
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
# Maven 运行单个测试类
|
|
708
|
-
mvn test -Dtest=AdServiceTest
|
|
709
|
-
|
|
710
|
-
# Maven 运行单个测试方法
|
|
711
|
-
mvn test -Dtest=AdServiceTest#testAddAd
|
|
249
|
+
```java
|
|
250
|
+
@Test @Tag("dev") public void testDev() { }
|
|
251
|
+
@Test @Tag("prod") public void testProd() { }
|
|
252
|
+
```
|
|
712
253
|
|
|
713
|
-
|
|
714
|
-
#
|
|
254
|
+
```bash
|
|
255
|
+
mvn test -Dgroups=dev # 运行 dev 标签
|
|
256
|
+
mvn test -DexcludedGroups=prod # 排除 prod 标签
|
|
715
257
|
```
|
|
716
258
|
|
|
717
259
|
---
|
|
718
260
|
|
|
719
|
-
##
|
|
720
|
-
|
|
721
|
-
### Q1: 什么时候使用 @SpringBootTest?
|
|
722
|
-
|
|
723
|
-
**A:**
|
|
724
|
-
- **不使用 @SpringBootTest**:工具类、枚举、POJO(纯逻辑测试,不需要 Spring 容器)
|
|
725
|
-
- **使用 @SpringBootTest**:Service、Controller、Mapper(需要依赖注入和 Spring 容器)
|
|
726
|
-
|
|
727
|
-
### Q2: Service 测试会污染数据库吗?
|
|
728
|
-
|
|
729
|
-
**A:** 不会。使用 `@SpringBootTest` + `@Transactional`,测试结束后自动回滚,不会污染数据库。
|
|
730
|
-
|
|
731
|
-
### Q3: 如何测试私有方法?
|
|
732
|
-
|
|
733
|
-
**A:**
|
|
734
|
-
- 不要直接测试私有方法
|
|
735
|
-
- 通过公共方法间接测试
|
|
736
|
-
- 如果私有方法太复杂,考虑提取为独立类
|
|
261
|
+
## 运行测试
|
|
737
262
|
|
|
738
|
-
|
|
263
|
+
```bash
|
|
264
|
+
mvn test # 运行所有测试
|
|
265
|
+
mvn test -Dtest=AdServiceTest # 运行单个测试类
|
|
266
|
+
mvn test -Dtest=AdServiceTest#testAddAd # 运行单个方法
|
|
267
|
+
mvn clean install -DskipTests # 跳过测试
|
|
268
|
+
```
|
|
739
269
|
|
|
740
|
-
|
|
741
|
-
- **单元测试**: 优先 Mock 外部依赖(快速、隔离)
|
|
742
|
-
- **集成测试**: 使用真实依赖(准确、完整)
|
|
743
|
-
- **平衡**: 既要有单元测试(快),也要有集成测试(准确)
|
|
270
|
+
---
|
|
744
271
|
|
|
745
|
-
|
|
272
|
+
## 开发检查清单
|
|
746
273
|
|
|
747
|
-
|
|
274
|
+
### 命名规范
|
|
748
275
|
|
|
749
|
-
|
|
276
|
+
- [ ] 测试类:`{被测试类名}Test`
|
|
277
|
+
- [ ] 测试方法:`test{功能}`
|
|
278
|
+
- [ ] 使用 `@DisplayName` 添加中文描述
|
|
279
|
+
- [ ] 位于 `src/test/java`,包路径与源码一致
|
|
750
280
|
|
|
751
|
-
|
|
752
|
-
- **单个测试**:使用 `@Disabled` 注解
|
|
753
|
-
- **Maven 跳过所有测试**:`mvn clean install -DskipTests`
|
|
281
|
+
### 注解选择
|
|
754
282
|
|
|
755
|
-
|
|
283
|
+
| 场景 | 注解组合 |
|
|
284
|
+
|------|---------|
|
|
285
|
+
| 工具类/枚举/POJO | 不加 `@SpringBootTest` |
|
|
286
|
+
| Service 测试 | `@SpringBootTest` + `@Transactional` |
|
|
287
|
+
| Controller 测试 | `@SpringBootTest` + `@AutoConfigureMockMvc` |
|
|
288
|
+
| Mock Spring Bean | `@MockBean` |
|
|
289
|
+
| Mock(纯单元测试) | `@Mock` + `@InjectMocks` |
|
|
290
|
+
|
|
291
|
+
### 常见错误
|
|
292
|
+
|
|
293
|
+
| 错误 | 正确做法 |
|
|
294
|
+
|------|---------|
|
|
295
|
+
| 测试在 `src/main/java` | 放到 `src/test/java` |
|
|
296
|
+
| 缺少 `@DisplayName` | 必须添加描述 |
|
|
297
|
+
| 需要注入但没加 `@SpringBootTest` | 加上注解 |
|
|
298
|
+
| `@SpringBootTest` 在非主包下 | 确保在主类同包或子包 |
|
|
299
|
+
| Mock 后不验证调用 | `Mockito.verify()` |
|
|
300
|
+
| 测试方法相互依赖 | 每个测试独立 |
|
|
301
|
+
| Service 测试不加 `@Transactional` | 加上防止污染数据库 |
|