项目测试总结
- 1. 单元测试
- JUnit
- 1.1 JUnit基本使用
- 1. 常用注解
- 2. Assert断言类
- 3. 使用步骤
- 在pom.xml中引入JUnit依赖
- 创建测试类和测试方法
- 4. 高级使用
- 超时测试
- 异常测试
- 参数化测试
- 5. 细节
- 1.2 Spring、Spring boot 整合 JUnit4
- 1. 引入pom依赖
- 2. 创建测试类
- spring项目
- springboot 项目
- 2. 接口测试
- 1. 常用接口测试方式
- 2. Knifej 基本使用
- 引入依赖
- Knife4j 相关配置
- 常用注解
- 限制请求方式
- 导出离线API文档
- 3. 并发测试
- Jmeter简介
- Jmeter的下载和安装
- 运行Jmeter
- 进行并发测试
1. 单元测试
JUnit
JUnit是一个Java语言的单元测试框架,Junit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何完成功能和完成什么样的功能,Junit是一套框架,继承TestCase类,就可以用Junit进行自动测试。
1.1 JUnit基本使用
1. 常用注解
@BeforeClass
:针对所有测试,只执行一次,且必须为static void
@Before
:初始化方法,执行当前测试类的每个测试方法前执行。
@Test
:修饰测试方法@Test(excepted=xxx.class)
:xxx.class 为异常类,表示测试的方法抛出此异常时,认为是正常通过测试 @Test(timeout = 毫秒数)
:测试方法执行时间是否符合预期
@After
:释放资源,执行当前测试类的每个测试方法后执行
@AfterClass
:针对所有测试,只执行一次,且必须为static void
@Ignore
:忽略的测试方法(只在测试类的时候生效,单独执行该测试方法无效)
@RunWith
:可以更改测试运行器 ,缺省值为: org.junit.runner.Runner
@Parameters
:参数化注解
2. Assert断言类
主要方法:
void assertEquals(boolean expected, boolean actual):检查预期值和实际值是否相等
void assertTrue(boolean condition):检查条件为真
void assertFalse(boolean condition):检查条件为假
void assertNotNull(Object object):判断对象是否不为空
void assertNull(Object object):判断对象是否不为空
void assertSame(Object expected, Object actual):判断两个对象是否指向同一个对象
void assertNotSame(Object expected, Object actual):判断两个对象是否不指向同一个对象
void assertArrayEquals(expectedArray, resultArray):判断两个数组是否相等
方法执行成功,则测试通过,执行失败则测试失败
3. 使用步骤
在pom.xml中引入JUnit依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<version>4.12</version>
</dependency>
创建测试类和测试方法
- 测试类的的命名规则一般是 xxxTest.java
- 测试方法上加上
@Test
注解。 - [快捷生成单元测试]
IDEA 中,选中当前类名,使用快捷键 ALT + ENTER(WIN)
,选择 Create Test
回车,即可进入生成测试类的选项中,再次回车,就快速的生成测试类。
- 生成的测试类在 src/test 目录下,而测试类和源代码的包名是一致的。
以下是测试类示例:
public class HelloServiceImplTest {
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void say() {
}
}
4. 高级使用
超时测试
通过timeout 参数和 @Test注释一起使用实现: @test(timeout)
如果一个测试用例比起指定的毫秒数花费了更多的时间,那么 Junit 将自动将它标记为失败。
示例:
//以下测试会失败,在一秒后会抛出异常 org.junit.runners.model.TestTimedOutException: test timed out after 1000 milliseconds
@Test(timeout = 1000)
public void testTimeout() throws InterruptedException {
TimeUnit.SECONDS.sleep(2);
System.out.println("Complete");
}
异常测试
可以测试代码是否它抛出了想要得到的异常。通过expected 参数和 @Test 注释一起使用实现: @Test(expected)
。
@Test(expected = NullPointerException.class)
public void testNullException() {
throw new NullPointerException();
}
参数化测试
Junit 4 引入了一个新的功能参数化测试。参数化测试允许开发人员使用不同的值反复运行同一个测试。
通过以下 5 个步骤来创建参数化测试。
- 用 @RunWith(Parameterized.class)来注释 test 类。
- 创建一个由 @Parameters 注释的公共的静态方法,它返回**一个对象的集合(数组)**来作为测试数据集合。
- 创建一个公共的构造函数,它接受和一行测试数据相等同的东西。
- 为每一列测试数据创建一个实例变量。
- 用实例变量作为测试数据的来源来创建你的测试用例。
示例:
//1.更改默认的测试运行器为RunWith(Parameterized.class)
@RunWith(Parameterized.class)
public class ParameterTest {
// 2.声明变量存放预期值和测试数据
private String firstName;
private String lastName;
//3.声明一个返回值 为Collection的公共静态方法,并使用@Parameters进行修饰
@Parameterized.Parameters //
public static List<Object[]> param() {
// 这里我给出两个测试用例
return Arrays.asList(new Object[][]{{"Mike", "Black"}, {"Cilcln", "Smith"}});
}
//4.为测试类声明一个带有参数的公共构造函数,并在其中为之声明变量赋值
public ParameterTest(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// 5. 进行测试,发现它会将所有的测试用例测试一遍
@Test
public void test() {
String name = firstName + " " + lastName;
assertThat("Mike Black", is(name));
}
}
5. 细节
- 一个单元测试类中各注解的执行顺序如下:
@BeforeClass –> @Before –> @Test–> @After –> @AfterClass
2. 每一个测试方法的调用顺序为:
@Before –> @Test –> @After
1.2 Spring、Spring boot 整合 JUnit4
Spring 框架提供了一个专门的测试模块(spring-test),用于应用程序的集成测试。 在 Spring Boot 中,你可以通过spring-boot-starter-test启动器快速开启和使用它。
1. 引入pom依赖
spring项目:
<!--junit依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--spring整合junit-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
springboot项目:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2. 创建测试类
spring项目
//使用Spring整合Junit专用的类加载器
@RunWith(SpringJUnit4ClassRunner.class)
//加载Spring配置文件或者配置类
@ContextConfiguration(classes = {SpringConfiguration.class}) //加载配置类
//@ContextConfiguration(locations={"classpath:applicationContext.xml"})//加载配置文件
public class AccountServiceTest {
//支持自动装配注入bean
@Autowired
private AccountService accountService;
@Test
public void testFindById(){
System.out.println(accountService.findById(1));
}
@Test
public void testFindAll(){
System.out.println(accountService.findAll());
}
}
以上方式中优化了原始Spring项目中JUnit单元测试类的测试方法总需要先创建IOC容器调用getBean获取对象的步骤,即以下重复代码:
ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
BookDao bookDao =(BookDao)ac.getBean("bookDao");
通过将Junit加载Spring的配置类,让Junit负责创建Spring容器,仅仅将配置文件的名称告诉它之后测试方法中需要进行测试的Bean就可以直接在测试类中进行注入
springboot 项目
// 获取启动类,加载配置,确定装载 Spring 程序的装载方法,它回去寻找 主配置启动类(被 @SpringBootApplication 注解的)
@SpringBootTest
// 让 JUnit 运行 Spring 的测试环境, 获得 Spring 环境的上下文的支持
@RunWith(SpringRunner.class)
public class EmployeeServiceImplTest {
// do
@Test
public void test(){
Calculate calculate = new Calculate();
int add = calculate.add(1, 2);
System.out.println(add);
}
}
2. 接口测试
1. 常用接口测试方式
Postman、Apifox等测试软件;
Knife4j:
Knife4j 是集Swagger2 和 OpenAPI3 为一体的增强解决方案,前身是 swagger-bootstrap-ui
,致力于 springfox-swagger
的增强 UI 实现。
2. Knifej 基本使用
引入依赖
主要与springboot的版本对应,以下依赖版本建议
- Spring Boot 版本建议 2.4.0~3.0.0之间
- Spring Boot 版本 < 2.4 版本则建议选择Knife4j 4.0之前的版本
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
Knife4j 相关配置
# springdoc-openapi项目配置 非必须
springdoc:
swagger-ui:
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha
api-docs:
path: /v3/api-docs
group-configs:
- group: 'default'
paths-to-match: '/**'
packages-to-scan: com.xiaominfo.knife4j.demo.web
# knife4j的增强配置
knife4j:
enable: true #此项必配置,否则knife4j运行有问题
setting:
language: zh_cn
常用注解
- @Api注解
添加在控制器类上的注解,通过此注解的tags属性可以修改原本显示控制器类名称的位置的文本,通常,建议在配置的tags属性值上添加序号,例如:“01. 用户模块”、“02. 微博模块”,则框架会根据值进行排序。
参数说明:
tags:配置模块名称
代码示例:
// 1. UserController
@Api(tags = "01.用户管理模块")
public class UserController {...}
// 2. WeiboController
@Api(tags = "02.微博管理模块")
public class WeiboController {...}
// 3. CommentController
@Api(tags = "03.评论管理模块")
public class CommentController {...}
文档效果(重启工程并刷新页面:http://localhost:8080/doc.html#/home)
- @ApiOperation注解
添加在控制器类中处理请求的方法上的注解,用于配置此方法处理的请求在API文档中显示的文本。
参数说明
value:配置业务名称
代码示例:
此处以注册功能为例,其他所有方法请添加说明
/**注册功能*/
@PostMapping("/reg")
@ApiOperation(value = "注册功能", notes="测试测试注册功能")
public int reg(@RequestBody UserRegDTO userRegDTO){...}
- @ApiModelProperty注解
是添加在实体类的属性上的注解,用于对请求参数或响应结果中的某个属性进行说明,
主要通过其value属性配置描述文本,并可通过example属性配置示例值。
参数说明
value属性:配置参数名称
required属性:配置是否必须提交此请求参数
example属性:配置示例值
注意:如果配置了 required=true,只是一种显示效果,Knife4j框架并不具备检查功能
代码示例
以注册功能UserRegDTO为例
@Data
public class UserRegDTO {
@ApiModelProperty(value = "用户名", required = true, example = "赵丽颖")
private String username;
@ApiModelProperty(value = "密码", required = true)
private String password;
@ApiModelProperty(value = "昵称", required = true)
private String nickname;
}
文档效果(重启工程并刷新页面:http://localhost:8080/doc.html#/home)
- @ApiImplicitParam注解
添加在控制器类中处理请求的方法上的注解,主要用于配置非封装(非XxxDTO/XxxParam的参数)的参数
参数说明
name:指定参数名称(参数变量名)
value:配置参数名称
dataType:配置数据类型
required:配置是否必须提交此请求参数
example:配置参数的示例值
注意:一旦使用此注解,各个参数的数据类型默认都会显示String,可以通过dataType指定数据类型
代码示例
此处以微博详情功能为例
@ApiImplicitParam(name = "id", value = "微博", required=true, dataType = "int")
public WeiboDetailVO selectById(int id){...}
- @ApiImplicitParams注解
添加在控制器类中处理请求的方法上的注解,当方法有多个非封装的参数时,在方法上添加此注解,并在注解内部通过@ApiImplicitParam数组配置多个参数。
代码示例:
/**微博详情页功能*/
@GetMapping("selectById")
@ApiOperation(value = "微博详情功能")
@ApiImplicitParams(value = {
@ApiImplicitParam(name = "id", value = "微博", required=true, dataType = "int"),
@ApiImplicitParam(name = "username", value = "用户名", required=true)
})
// 额外增加username参数,仅仅用于测试
public WeiboDetailVO selectById(int id, String username){
return weiboMapper.selectById(id);
}
// 带上传的参数的情况
@ApiOperation(value = "uploadExcel", notes = "上传excel文件进行解析")
@ApiImplicitParams(
{ @ApiImplicitParam(value = "上传文件", required = true)
})
@PostMapping("/uploadExcel")
public HttpResp<String> uploadExcel(@RequestPart @RequestParam("excel")MultipartFile excel) {
String originalFilename = excel.getOriginalFilename();
log.debug("上传文件名称: {}",originalFilename);
return new HttpResp<>(200,"success",originalFilename+"上传成功",LocalDate.now());
}
文档效果(重启工程并刷新页面:http://localhost:8080/doc.html#/home)
- @ApiIgnore注解
添加在处理请求的方法的参数上,用于表示API文档框架应该忽略此参数
代码示例
// 参数中添加@ApiIgnore注解
public int insert(@RequestBody WeiboDTO weiboDTO, @ApiIgnore HttpSession session){...}
限制请求方式
API文档中默认每个功能会展示7种请求方式,遵循RESTful规则将 @RequestMapping 注解修改为对应请求方法的注解,比如:@GetMapping @PostMapping @PutMapping @DeleteMapping 注解,重启工程后刷新测试。
导出离线API文档
文档管理 - 离线文档 中存在多种格式的导出格式
选择合适的文档格式,导出即可到本地磁盘
3. 并发测试
Jmeter简介
Apache JMeter是Apache组织开发的基于Java的压力测试工具。JMeter 可以用于对服务器、网络或对象模拟巨大的负载,来自不同压力类别下测试它们的强度和分析整体性能。
另外,JMeter能够对应用程序做功能/回归测试,通过创建带有断言的脚本来验证你的程序返回了你期望的结果。
Jmeter的下载和安装
下载地址:https://jmeter.apache.org/download_jmeter.cgi
运行Jmeter
进入安装路径的bin目录下执行 jmeter.bat
进行并发测试
- 创建线程组:右键测试计划,添加一个线程组,可以设置对应多少秒有多少个线程数来访问这个接口
- 添加HTTP请求
- 添加Http请求的响应
- 设置Http请求路径
- 查看结果