Spring Boot框架下的单元测试
1. 什么是单元测试
1.1 基本定义
- 单元测试(Unit Test) 是对软件开发中最小可测单位(例如一个方法或者一个类)进行验证的一种测试方式。
- 在 Java 后端的 Spring Boot 项目中,单元测试通常会借助 JUnit、Mockito 等框架对代码中核心逻辑进行快速且隔离的验证,保证功能正确性。
目的:及早发现并修复 BUG,使后续迭代功能或重构时能迅速验证不会破坏已实现的功能。
1.2 单元测试在 Spring Boot 中的地位
- Spring Boot 提供了非常方便的测试支持,如
@SpringBootTest
、@TestConfiguration
等注解,让开发者可以快速地在带有 Spring 容器上下文的环境中执行测试。 - Spring Boot 本身也对 JUnit、Mockito、AssertJ 等常用测试框架或库提供了开箱即用的整合或依赖。
1.3 单元测试与其他测试的区别
- 单元测试:聚焦在一个方法或者一个类层面,不涉及过多外部依赖,能极快地发现逻辑错误。
- 集成测试:多个模块或组件交互时的测试,通常依赖真实数据库、消息队列等外部资源。
- 端到端测试(E2E):关注的是整个系统的完整流程,包括前端、后端、数据库、外部接口等。
- 在 Spring Boot 环境中,可以使用
@SpringBootTest
搭配 Mock 或者内存数据库来实现集成测试,但这通常已经不只是“单元”级别了。
2. 为什么要写单元测试?
- 快速发现 Bug:写完代码马上测,不用等到上线才被发现问题。
- 减少回归成本:以后代码改动或升级,只要一键跑测试,就能知道改动有没有影响其他功能。
- 保证代码质量:养成单元测试的习惯,会促使你把代码设计得更简洁和更容易测试。
简单说:花小时间写单元测试,能为你省下大时间修 Bug。
3. 环境准备
3.1 依赖
在一个常规的 Spring Boot 项目中,只要在 pom.xml
(Maven)或 build.gradle
(Gradle) 里加上:
<!-- 如果是 Maven -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
- JUnit 5:最常用的Java测试框架(写
@Test
方法) - Mockito:常用的“模拟”库(用来Mock其他依赖)
- AssertJ / Hamcrest:更好用的断言库
- Spring Test / Spring Boot Test:Spring官方提供的测试辅助
这也就够了,一般不需要额外安装别的。
3.2 项目结构
Spring Boot常见的目录结构(Maven示例):
src├─ main│ └─ java│ └─ com.example.demo│ ├─ DemoApplication.java│ └─ service│ └─ MyService.java└─ test└─ java└─ com.example.demo├─ DemoApplicationTests.java└─ service└─ MyServiceTest.java
src/main/java
放你的业务代码。src/test/java
放你的测试代码。- 通常测试类的包路径要和被测类一致,这样在IDE里能很快对上号,也方便管理。
4. 最最简单的单元测试示例(不依赖Spring)
先从“纯JUnit”说起,最简单的情况就是:
- 我有一个普通的工具类/方法
- 我就想测试它的输入输出对不对
- 不用装载Spring,也不用什么复杂注解
代码示例
假设我们有一个简单的工具类:
public class MathUtil {public static int add(int a, int b) {return a + b;}
}
那我们写一个测试类(路径:src/test/java/.../MathUtilTest.java
):
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;public class MathUtilTest {@Testvoid testAdd() {int result = MathUtil.add(2, 3);Assertions.assertEquals(5, result, "2 + 3 应该等于 5");}
}
@Test
表示这是一个测试方法。Assertions.assertEquals(期望值, 实际值, "提示信息")
用来断言。- 如果断言不通过,测试就失败;通过则测试成功。
运行方法:
- 在 IDE(如 IntelliJ/ Eclipse)里,右键这个
MathUtilTest
类 -> Run 'MathUtilTest' - 或者在命令行里运行
mvn test
(Maven) /gradle test
(Gradle)。
这就是最最基础的单元测试。
5. 在 Spring Boot 里测试 - Service层
当你要测试一个 Service(业务逻辑类) 时,它可能依赖其他Bean(例如 Repository、Dao 等)或者需要 Autowired。在 Spring Boot 里,有两种主要方法:
方法1:纯Mock(不启动Spring Context)
适合只想测试这个Service逻辑本身,不需要真的连数据库,也不需要整个Spring环境。速度最快。
- 用 Mockito 来创建一个假的(Mock)依赖。
- 注入到要测的Service里,这样你可以控制依赖的行为。
示例
UserRepository.java (假设它是个接口,用来访问数据库):
public interface UserRepository {User findByName(String name);// ... 其他方法
}
UserService.java (我们要测这个类):
public class UserService {private UserRepository userRepository;// 通过构造注入依赖public UserService(UserRepository userRepository) {this.userRepository = userRepository;}public String getUserNickname(String name) {User user = userRepository.findByName(name);if (user == null) {return "UNKNOWN";}return user.getNickname();}
}
UserServiceTest.java (测试类,不依赖 Spring):
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Assertions;
import org.mockito.Mockito;
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;@ExtendWith(MockitoExtension.class) // JUnit5 启用Mockito
public class UserServiceTest {@Mockprivate UserRepository userRepository; // Mock出来的依赖@InjectMocksprivate UserService userService; // 要测试的对象,会把上面这个Mock自动注入进来@Testvoid testGetUserNickname_found() {// 1. 假设我们模拟一个“数据库中查到的用户”:User mockUser = new User();mockUser.setName("alice");mockUser.setNickname("AliceWonder");// 2. 定义假数据的返回行为Mockito.when(userRepository.findByName("alice")).thenReturn(mockUser);// 3. 调用被测方法String nickname = userService.getUserNickname("alice");// 4. 断言结果Assertions.assertEquals("AliceWonder", nickname);}@Testvoid testGetUserNickname_notFound() {// 没有设置when,则默认返回nullString nickname = userService.getUserNickname("bob");Assertions.assertEquals("UNKNOWN", nickname);}
}
- 使用了
@Mock
注解声明要模拟的依赖userRepository
。 - 使用了
@InjectMocks
注解告诉 Mockito,要把所有标记@Mock
的对象注入进UserService
。 - 这样就能让
UserService
这个对象在执行时使用模拟过的userRepository
而不访问真实数据库。 - 然后通过
Mockito.when(...)
来定义依赖方法的返回值,用于测试用例的前提条件设置。 - 通过
Assertions
来验证执行结果是否符合预期。
这样就只测 UserService
的逻辑,不会真的访问数据库,也不需要启动Spring,执行很快。
方法2:使用 @SpringBootTest
(集成上下文)
适合你想在测试时使用Spring管理Bean,比如自动注入
@Autowired
,或想测试和别的Bean的连接配置是否正常。
- 在测试类上加
@SpringBootTest
。 - 这样Spring容器会启动,你也能
@Autowired
你的Service或者别的Bean。
示例
UserService.java (类似前面,只不过换成了 Spring注入):
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public String getUserNickname(String name) {User user = userRepository.findByName(name);if (user == null) {return "UNKNOWN";}return user.getNickname();}
}
UserServiceSpringTest.java (测试类,使用Spring上下文):
@SpringBootTest
public class UserServiceSpringTest {@Autowiredprivate UserService userService;@MockBeanprivate UserRepository userRepository; // @MockBean的意思:Spring 启动时,// 把真正的UserRepository替换成一个Mock对象,// 我们就可以定义它的返回值,而不会真的连数据库@Testvoid testGetUserNickname_found() {User mockUser = new User();mockUser.setName("alice");mockUser.setNickname("AliceWonder");Mockito.when(userRepository.findByName("alice")).thenReturn(mockUser);String result = userService.getUserNickname("alice");Assertions.assertEquals("AliceWonder", result);}@Testvoid testGetUserNickname_notFound() {// 不设置when就会返回nullString result = userService.getUserNickname("unknown");Assertions.assertEquals("UNKNOWN", result);}
}
@SpringBootTest
会启动一个小型Spring环境,让@Autowired
能起作用。@MockBean
可以让你把某个Bean(比如UserRepository
)变成一个模拟对象。- 整体执行依然比较快,但比纯Mock稍微慢一点,因为要先启动Spring容器。
6. 测试 Controller 层
在 Spring Boot 里,Controller 是对外的 HTTP 接口。最常见的两种测试方式:
- 用
@WebMvcTest
+MockMvc
:不启动整个应用,只启动Web层,速度较快;- 用
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+TestRestTemplate
:会真正启动一个内嵌服务器,发起真实HTTP请求,更贴近实际环境。
6.1 @WebMvcTest
示例
@WebMvcTest(UserController.class) // 表示只测 UserController 相关
public class UserControllerTest {@Autowiredprivate MockMvc mockMvc; // 用来模拟HTTP请求@MockBeanprivate UserService userService; // Mock掉Service层@Testvoid testGetUser() throws Exception {// 假设Service返回一个User对象User mockUser = new User();mockUser.setName("test");mockUser.setNickname("TestNick");// 定义service行为Mockito.when(userService.getUserNickname("test")).thenReturn("TestNick");// 用MockMvc发起GET请求,对应Controller的 /user/{name} 路径mockMvc.perform(MockMvcRequestBuilders.get("/user/test")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().string("TestNick"));}
}
@WebMvcTest
只会扫描和加载 Web 层相关的组件,不会启动整个 Spring Boot 应用,测试速度更快。mockMvc.perform(get("/users/1"))
可以模拟一次 GET 请求到/users/1
,并断言返回的 JSON 结构和内容。
6.2 @SpringBootTest
+ TestRestTemplate
如果你想做一个更真实的集成测试(包括 Controller、Service、Repository 等所有层),可以使用 @SpringBootTest
并设置 webEnvironment = RANDOM_PORT
或 DEFINED_PORT
来启动内嵌服务器,然后注入 TestRestTemplate
来请求:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerIntegrationTest {@Autowiredprivate TestRestTemplate restTemplate; // 可以真的发请求@Testvoid testGetUser() {// 假设数据库里已经有对应数据,或者你用 @MockBean 替换依赖String result = restTemplate.getForObject("/user/test", String.class);Assertions.assertEquals("TestNick", result);}
}
- 这里会真正启动一个随机端口的Tomcat,然后
TestRestTemplate
真的去请求本地这个/user/test
接口。 - 非常贴近真实部署,只是适合做集成测试,比前面的MockMvc测试稍慢一点。
7. 常见的断言与技巧
7.1 断言
Assertions.assertEquals(期望, 实际)
:断言二者相等。Assertions.assertTrue(条件)
:断言条件为真。Assertions.assertThrows(异常类型, 代码块)
:断言执行代码块会抛出指定异常。
例如:
@Test
void testThrowException() {Assertions.assertThrows(IllegalArgumentException.class, () -> {// 假设调用了一个会抛出异常的方法someMethod(null);});
}
7.2 Mock时常用的 Mockito 方法
Mockito.when( mockObj.方法(...) ).thenReturn(返回值);
Mockito.when( mockObj.方法(...) ).thenThrow(异常);
Mockito.verify( mockObj, Mockito.times(1) ).某方法(...)
; // 验证是否调用了某方法
8. 测试运行与整合
8.1 在本地IDE里运行
- 右键单个测试类或测试方法 -> Run
- 或者在项目主目录运行
mvn test
/gradle test
8.2 与持续集成(CI)整合
- 在 Jenkins、GitLab CI、GitHub Actions 等环境里,一般只要执行
mvn test
或gradle test
就可以跑所有测试用例。 - 如果测试全部通过,就说明代码基本没问题;如果测试挂了,说明你这次提交的改动有Bug或者破坏了原有逻辑。
9. 流程小结(简版“使用指南”)
-
新手首次写单元测试:
- 在
src/test/java
下创建和源代码同包路径的测试类:XXXTest.java
。 - 在类里加
@Test
注解的方法,里面写Assertions.assertXXX(...)
。 - 右键运行,看输出是否通过。
- 在
-
要测Service逻辑,但不想连数据库:
- 在测试类上写:
@ExtendWith(MockitoExtension.class) public class MyServiceTest {@Mockprivate MyRepository myRepository;@InjectMocksprivate MyService myService;... }
- 用
Mockito.when(...)
来模拟依赖。 - 用
assertEquals(...)
来判断结果。
- 在测试类上写:
-
要测Service逻辑,并用Spring上下文:
- 在测试类上加
@SpringBootTest
。 - 注入 Service:
@Autowired private MyService myService;
- 如果你不想真的连数据库,那就用
@MockBean MyRepository myRepository;
- 在测试类上加
-
要测Controller:
- 用
@WebMvcTest(MyController.class)
+@MockBean MyService myService;
+MockMvc
做单元测试,速度较快; - 或者用
@SpringBootTest(webEnvironment = ... )
+TestRestTemplate
做近似真实的集成测试。
- 用
10. 其他常见问题
- 测试和生产环境的配置冲突了怎么办?
- 可以在
application-test.yml
里放测试专用配置,然后在测试时用spring.profiles.active=test
。
- 可以在
- 需要数据库的测试怎么办?
- 可以用
@DataJpaTest
+内存数据库(比如 H2),只测JPA相关逻辑,不影响真数据库。
- 可以用
- 想看覆盖率怎么办?
- 可以集成 Jacoco 插件,跑
mvn test
后生成覆盖率报告,看你的测试是不是覆盖到了主要逻辑。
- 可以集成 Jacoco 插件,跑
- 测试很慢怎么办?
- 如果你的逻辑不是必须要Spring,就尽量用纯Mock,不用
@SpringBootTest
。 - 如果只是测Controller,就用
@WebMvcTest
,不要启动全部。
- 如果你的逻辑不是必须要Spring,就尽量用纯Mock,不用
相关文章:
Spring Boot框架下的单元测试
1. 什么是单元测试 1.1 基本定义 单元测试(Unit Test) 是对软件开发中最小可测单位(例如一个方法或者一个类)进行验证的一种测试方式。在 Java 后端的 Spring Boot 项目中,单元测试通常会借助 JUnit、Mockito 等框架对代码中核心逻辑进行快…...

AI测试工程师成长指南:以DeepSeek模型训练为例
目录 引言:AI测试工程师的使命与挑战成长日记:从测试小白到AI测试专家核心能力:AI测试工程师的必备素养知识体系:技术栈与技能图谱AI测试工具全景:以DeepSeek为核心的工具链实战训练模式:以DeepSeek模型迭…...
【数据结构】_队列的结构与实现
目录 1. 队列的概念和结构 2. 队列的应用 3. 队列的实现 3.1 队列实现的底层结构选择 3.2 结构体设计 3.2.1 仅为链表结点设计结构体 3.2.2 为链表再设计一个结构体 3.3 Queue.h 3.4 Queue.c 3.5 Test_Queue.c 注:部分方法实现细节 1. 队列的概念和结构 …...

机器学习--2.多元线性回归
多元线性回归 1、基本概念 1.1、连续值 1.2、离散值 1.3、简单线性回归 1.4、最优解 1.5、多元线性回归 2、正规方程 2.1、最小二乘法 2.2、多元一次方程举例 2.3、矩阵转置公式与求导公式 2.4、推导正规方程0的解 2.5、凸函数判定 成年人最大的自律就是:…...

MySQL时间类型相关总结(DATETIME, TIMESTAMP, DATE, TIME, YEAR)
MySQL时间类型相关总结(DATETIME, TIMESTAMP, DATE, TIME, YEAR) MySQL官方文档: https://dev.mysql.com/doc/refman/8.0/en/date-and-time-types.html 一. 对比: 在 MySQL 中,处理时间相关的数据类型主要有以下几种:DATE、TIME、…...

朴素贝叶斯原理
在所有的机器学习分类算法中,朴素贝叶斯和其他绝大多数的分类算法都不同。对于大多数的分类算法,比如决策树,KNN,逻辑回归,支持向量机等,他们都是判别方法,也就是直接学习出特征输出Y和特征X之间的关系,要么…...
k8s中,一.pod污点,二.pod容器污点容忍策略,三.pod优先级(PriorityClass类)
一.pod污点:污点是让节点与pod产生排斥的一类规则污点标签的命令1.查看污点标签kubectl describe nodes 节点名2.设置污点标签kubectl taint node 节点名 key值value值:污点标签种类3.删除污点标签kubectl taint node 节点名 key值value值:污点标签种类-4.污点标签种类驱逐:NoE…...

【重生之学习C语言----水仙花篇】
目录 编辑 ----------------------------------------begin-------------------------------------- 一、什么是水仙花数? 二、问题分析 确定数字的位数:计算输入数字的位数 n。 分离每一位数字:例如将 153 分离为 1、5、3。 计算各…...

两步构建 AI 总结助手,实现智能文档摘要
在信息极度丰富的当下,如何从海量且复杂的文件资料中筛选出关键内容,成为了不少企业和个人急需解决的问题。本次解决方案将向您介绍,如何通过函数计算 FC 阿里云百炼平台搭建智能 AI 总结助手,实现高效的文本自动总结和信息提取。…...

承压金字塔(蓝桥杯17C)
文件读取,与写入:C 文件和流 | 菜鸟教程 #include <iostream> #include <fstream> #include <string> using namespace std; double sum[30][30]; int main() {ifstream infile("C:\\Users\\xutianci\\OneDrive\\Desktop\\TMOCC\…...
day33-数据同步rsync
一、Rsync本地模式和远程模式 纯通过rsync的命令,来实现,数据目录A 拷贝到数据目录B 也就是模拟cp的用法 很简单 1.安装 yum install rsync -y 2.命令语法,分几个模式 - 本地模式 rsync 参数 源路径 目标路径 rsync -xxxxx /var…...
Android 实现首页Tab切换并且支持懒加载功能详解
目录 1. 添加依赖2. 布局文件3. 创建 Fragment4. 创建适配器5. 在 MainActivity 中设置 TabLayout 和 ViewPager2 1. 添加依赖 在 build.gradle 文件中添加以下依赖: implementation androidx.viewpager2:viewpager2:1.1.0-beta01 implementation com.google.andr…...

[Android] 360行车记录仪谷歌版
[Android] 360行车记录仪谷歌版 链接:https://pan.xunlei.com/s/VOIQYq-jmW8Jpb8y3EIA3YdtA1?pwd3abw# 新买的360行车记录仪,配套软件让安装360智慧生活软件,二百多兆,各种功能齐全、忒齐全,好多用不到,…...
基于Redis分布式锁
1. 获取锁的过程 使用SETNX命令:SETNX(SET if Not eXists)是一个原子操作,它会在指定的key不存在时,将key的值设置为给定的value,并返回1;如果key已经存在,则不做任何操作࿰…...
Spring Boot 条件注解:@ConditionalOnProperty 完全解析
在 Spring Boot 项目中,有时候我们希望根据配置文件中的某个属性值来决定是否启用某个功能或加载某个组件。此时,ConditionalOnProperty 注解就可以发挥作用。它通过配置文件的属性值控制 Bean 或配置类的加载,使得我们的程序更具灵活性。 本…...
canny边缘检测
Canny边缘检测算法是一种广泛使用的边缘检测方法,由John F.Canny在1986年提出。它被认为是边缘检测的“黄金标准”,因为它在检测边缘的同时能够很好地抑制噪声,并且能够精确地定位边缘。Canny算法通过一系列步骤来实现鲁棒的边缘检测…...

团建 蓝桥杯省a 15
问题描述 小蓝正在和朋友们团建,有一个游戏项目需要两人合作,两个人分别拿到一棵大小为 nn 和 mm 的树,树上的每个结点上有一个正整数权值。 两个人需要从各自树的根结点 1 出发走向某个叶结点,从根到这个叶结点的路径上经过的所…...
【逻辑学导论】1.6 有效性和真实性
当一个演绎论证成功地将结论和前提必然地联系起来,它是有效的。有效性是针对论证的各命题之间的关系而言的。一个论证是有效的,当且仅当它不可能有真前提和假结论,当且仅当其结论是从其前提逻辑必然地推导出来的。因此,有效性永远…...

IDEA 中集成 Maven,配置环境、创建以及导入项目
目录 在 IntelliJ IDEA 中集成 Maven 并配置环境 1. 打开 IDEA 设置 2. 定位 Maven 配置选项 3. 配置 Maven 路径 4. 应用配置 创建 Maven 项目 1. 新建项目 2. 选择项目类型 3. 配置项目信息 4. 确认 Maven 设置 5. 完成项目创建 导入 Maven 项目 1. 打开导入窗口…...

Qt跨屏窗口的一个Bug及解决方案
如果我们希望一个窗口覆盖用户的整个桌面,此时就要考虑用户有多个屏幕的场景(此窗口要横跨多个屏幕),由于每个屏幕的分辨率和缩放比例可能是不同的,Qt底层在为此窗口设置缩放比例(DevicePixelRatio…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...

Xela矩阵三轴触觉传感器的工作原理解析与应用场景
Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...
基于鸿蒙(HarmonyOS5)的打车小程序
1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...

【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...