开发实用篇(二)
在启动测试环境时可以通过 properties 参数设置测试环境专用的属性
@SpringBootTest(properties = {"test.prop=testValue1"})
public class PropertiesAndArgsTest {
@Value("${test.prop}")
private String msg;
@Test
void testProperties() {
System.out.println("msg = " + msg);
}
}
优势:比多环境开发中的测试环境影响范围更小,仅对当前测试类有效
在启动测试环境时可以通过args参数设置测试环境专用的传入参数
// args属性可以为当前测试用例添加临时的命令行参数
@SpringBootTest(args = {"--test.prop=testValue2"})
public class PropertiesAndArgsTest {
@Value("${test.prop}")
private String msg;
@Test
void testProperties() {
System.out.println("msg = " + msg);
}
}
当 args参数 与 properties参数 设置共存时, args属性配置优先于properties属性配置加载。
小结
加载测试临时属性应用于小范围测试环境加载测试专用配置步骤①:在测试包test中创建专用的测试环境配置类
@Configuration
public class MsgConfig {
@Bean
public String msg() {
return "bean msg";
}
}
上述配置仅用于演示当前实验效果,实际开发可不能这么注入String类型的数据
步骤②:在启动测试环境时,导入测试环境专用的配置类,使用 @Import
注解即可实现
@SpringBootTest
@Import({MsgConfig.class})
public class ConfigurationTest {
@Autowired
private String msg;
@Test
void testConfiguration() {
System.out.println("msg = " + msg);
}
}
小结
加载测试范围配置应用于小范围测试环境Web环境模拟测试模拟端口
每一个springboot的测试类上方都会标准@SpringBootTest注解,而注解带有一个属性,叫做webEnvironment。通过该属性就可以设置在测试用例中启动web环境,具体如下:@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)测试类中启动web环境时,可以指定启动的Web环境对应的端口,springboot提供了4种设置值,分别如下:MOCK:根据当前设置确认是否启动web环境,例如使用了Servlet的API就启动web环境,属于适配性的配置DEFINED_PORT:使用自定义的端口作为web服务器端口RANDOM_PORT:使用随机端口作为web服务器端口NONE:不启动web环境虚拟请求测试新建
public class WebTest {
@Test
void test() {
}
}
BookController
类@RestController虚拟请求测试
@RequestMapping("/books")
public class BookController {
@GetMapping
public String getById() {
System.out.println("getById is running... ");
return "springboot";
}
}
// 1.使用 webEnvironment 属性开启web环境匹配响应执行状态
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
// 2.开启虚拟MVC调用
@AutoConfigureMockMvc
public class WebTest {
// 注入虚拟MVC调用对象
@Test
void testWeb(@Autowired MockMvc mvc) throws Exception {
// http://localhost:8080/books
// 3.创建虚拟请求,当前访问/books
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
// 执行对应的请求
mvc.perform(builder);
}
}
虚拟请求状态匹配
// 注入虚拟mvc调用对象匹配失败的信息提示如:匹配响应体
@Test
void testStatus(@Autowired MockMvc mvc) throws Exception {
// 创建虚拟请求,当前访问/books
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
// 执行请求
ResultActions actions = mvc.perform(builder);
// 设定预期值 与真实值进行比较;成功测试通过,失败测试失败
// 定义本次调用的预期值
StatusResultMatchers status = MockMvcResultMatchers.status();
// 预计本次调用时成功的:状态200
ResultMatcher ok = status.isOk();
// 添加预计值到本次调用过程中进行匹配
actions.andExpect(ok);
}
响应体匹配(非json数据格式)
// 注入虚拟mvc调用对象匹配失败的信息提示如:匹配响应体(json)
@Test
void testBody(@Autowired MockMvc mvc) throws Exception {
// 创建虚拟请求,当前访问/books
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
// 执行请求
ResultActions actions = mvc.perform(builder);
// 设定预期值 与真实值进行比较;成功测试通过,失败测试失败
// 定义本次调用的预期值
ContentResultMatchers content = MockMvcResultMatchers.content();
ResultMatcher result = content.string("springboot");
// 添加预计值到本次调用过程中进行匹配
actions.andExpect(result);
}
响应体匹配(json数据格式,开发中的主流使用方式)
创建 book 实体类@Data修改
public class Book {
private int id;
private String name;
private String type;
private String description;
}
BookController
类@RestController测试方法
@RequestMapping("/books")
public class BookController {
@GetMapping
public String getById() {
System.out.println("getById is running... ");
return "springboot";
}
@GetMapping("{id}")
public Book getById(@PathVariable int id) {
System.out.println("getById is running... ");
Book book = new Book();
book.setId(1);
book.setName("springboot");
book.setType("springboot");
book.setDescription("springboot");
return book;
}
}
// 注入虚拟mvc调用对象匹配失败的信息提示如:匹配响应头
@Test
void testJson(@Autowired MockMvc mvc) throws Exception {
// 创建虚拟请求,当前访问/books
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/1");
// 执行请求
ResultActions actions = mvc.perform(builder);
// 设定预期值 与真实值进行比较;成功测试通过,失败测试失败
// 定义本次调用的预期值
ContentResultMatchers content = MockMvcResultMatchers.content();
ResultMatcher result = content.json("{\"id\":1,\"name\":\"springboot\",\"type\":\"springboot\",\"description\":\"springboot\"}");
// 添加预计值到本次调用过程中进行匹配
actions.andExpect(result);
}
虚拟请求响应头匹配
// 注入虚拟mvc调用对象匹配失败的信息提示如:基本上齐了,头信息,正文信息,状态信息都有了,就可以组合出一个完美的响应数据比对结果了。以下范例就是三种信息同时进行匹配校验,也是一个完整的信息匹配过程。
@Test
void testContentType(@Autowired MockMvc mvc) throws Exception {
// 创建虚拟请求,当前访问/books
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/1");
// 执行请求
ResultActions actions = mvc.perform(builder);
// 设定预期值 与真实值进行比较;成功测试通过,失败测试失败
// 定义本次调用的预期值
HeaderResultMatchers header = MockMvcResultMatchers.header();
ResultMatcher contentType = header.string("Content-Type", "application/json");
// 添加预计值到本次调用过程中进行匹配
actions.andExpect(contentType);
}
@Test业务层测试事务回滚
void testGetById(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/1");
ResultActions action = mvc.perform(builder);
StatusResultMatchers status = MockMvcResultMatchers.status();
ResultMatcher ok = status.isOk();
action.andExpect(ok);
HeaderResultMatchers header = MockMvcResultMatchers.header();
ResultMatcher contentType = header.string("Content-Type", "application/json");
action.andExpect(contentType);
ContentResultMatchers content = MockMvcResultMatchers.content();
ResultMatcher result = content.json("{\"id\":1,\"name\":\"springboot\",\"type\":\"springboot\"}");
action.andExpect(result);
}
为测试用例添加事务,SpringBoot 会对测试用例对应的事务提交操作进行回滚
@SpringBootTest如果想在测试用例中提交事务,可以通过
@Transactional
public class ServiceTest {
@Autowired
private BookService bookService;
@Test
void testSave() {
Book book = new Book();
book.setName("springboot5");
book.setType("springboot5");
book.setDescription("springboot5");
bookService.save(book);
}
}
@Rollback
注解设置回滚状态为false
即可正常提交事务@SpringBootTest
@Transactional
@Rollback(false)
public class ServiceTest {
@Autowired
private BookService bookService;
@Test
void testSave() {
Book book = new Book();
book.setName("springboot5");
book.setType("springboot5");
book.setDescription("springboot5");
bookService.save(book);
}
}
小结
在 springboot 的测试类中通过添加注解 @Transactional 来阻止测试用例提交事务通过注解 @Rollback 控制 springboot 测试类执行结果是否提交事务,需要配合注解@Transactional 使用测试用例设置随机数据测试用例数据通常采用随机值进行测试,使用 SpringBoot 提供的随机数为其赋值
①: yml 中设置随机值
testcase:
book:
id: ${random.int}
id2: ${random.int(10)}
type: ${random.int!5,10!}
name: ${random.value}
uuid: ${random.uuid}
publishTime: ${random.long}
②: 创建BookCase
类封装数据
// 1.定义数据模型封装yaml文件中对应的数据
// 2.定义为spring管控的bean
@Component
@Data
// 3.指定加载的数据
@ConfigurationProperties(prefix = "testcase.book")
public class BookCase {
private int id;
private int id2;
private int type;
private String name;
private String uuid;
private long publishTime;
}
③: 测试
@SpringBootTest
public class BookCaseRandom {
@Autowired
private BookCase bookCase;
@Test
void testProperties() {
System.out.println(bookCase);
}
}
对于随机值的产生,还有一些小的限定规则,比如产生的数值性数据可以设置范围等,具体如下:
${random.int}
表示随机整数${random.int(10)}
表示10以内的随机数${random.int(10,20)}
表示10到20的随机数其中( )
可以是任意字符,例如[ ]、!!、@@
均可小结
使用随机数据替换测试用例中书写固定的数据数据层解决方案内置数据源 Hikari现有数据层解决方案技术选型Mysql+Druid+MyBatisPlus
目前我们使用的数据源技术是Druid,运行时可以在日志中看到对应的数据源初始化信息,具体如下:
如果不配置数据源默认是HikariDataSource
数据源配置格式
格式一 (通用配置未声明数据源技术)默认是hikari
数据源spring:格式二 (配置指定的数据源需要导入对应的 starter)
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
username: root
password: 283619
# druid 数据源配置
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
username: root
password: 283619
# hikari 数据源配置 url地址要单独配置 并且要去除druid数据源的依赖
spring:
datasource:
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
hikari:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 283619
数据源配置
springboot 提供了3款内嵌数据源
技术,分别如下:
通用配置无法设置具体的数据源配置信息,仅提供基本的连接相关配置,如需配置,在下一级配置中设置具体设定
spring:JdbcTemplate
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC
username: root
password: 283619
hikari:
maximum-pool-size: 50
内置持久化
解决方案——JdbcTemplate
步骤①:导入jdbc对应的坐标,记得是starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
步骤②:自动装配JdbcTemplate对象
@SpringBootTest
class Springboot15SqlApplicationTests {
@Test
void testJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate){
}
}
步骤③:使用JdbcTemplate实现查询操作(非实体类封装数据的查询操作)
@Test
void testJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate) {
String sql = "select * from tbl_book";
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
System.out.println(maps);
}
步骤④:使用JdbcTemplate实现查询操作(实体类封装数据的查询操作)
@Test
void testJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate) {
String sql = "select * from tbl_book";
RowMapper<Book> rm = new RowMapper<Book>() {
@Override
public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
Book temp = new Book();
temp.setId(rs.getInt("id"));
temp.setName(rs.getString("name"));
temp.setType(rs.getString("type"));
temp.setDescription(rs.getString("description"));
return temp;
}
};
List<Book> list = jdbcTemplate.query(sql, rm);
System.out.println(list);
}
步骤⑤:使用JdbcTemplate实现增删改操作
@Test
void testJdbcTemplateSave(@Autowired JdbcTemplate jdbcTemplate) {
String sql = "insert into tbl_book values(null,'springboot1','springboot2','springboot3')";
jdbcTemplate.update(sql);
}
如果想对 JdbcTemplate 对象进行相关配置,可以在yml文件中进行设定,具体如下:
spring:
jdbc:
template:
query-timeout: -1 # 查询超时时间
max-rows: 500 # 最大行数
fetch-size: -1 # 缓存行数
小结
SpringBoot内置JdbcTemplate持久化解决方案使用JdbcTemplate需要导入spring-boot-starter-jdbc的坐标H2数据库SpringBoot提供了3种内嵌数据库供开发者选择,提高开发测试效率
H2HSQLDerby内嵌数据库(H2)
步骤①:导入H2数据库对应的坐标,一共2个
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
步骤②:将工程设置为web工程,启动工程时启动H2数据库
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
步骤③:通过配置开启H2数据库控制台访问程序,也可以使用其他的数据库连接软件操作
# h2 数据库
server:
port: 80
spring:
h2:
console: # 控制台模式
enabled: true # 设置为true表示开启
path: /h2 # 访问路径
web端访问路径/h2,访问密码123456
,如果访问失败,先配置下列数据源,启动程序运行后再次访问/h2路径就可以正常访问了
datasource:操作数据库(创建表)
url: jdbc:h2:~/test
hikari:
driver-class-name: org.h2.Driver
username: sa
password: 123456
create table tbl_book (id int,name varchar,type varchar,description varchar)
步骤④:使用 JdbcTemplate 或 MyBatisPlus 技术操作数据库
记得测试的时候 要web服务器给关了,不然会报错
@SpringBootTestH2数据库控制台仅用于开发阶段,线上项目请务必关闭控制台功能SpringBoot 可以根据url地址自动识别数据库种类,在保障驱动类存在的情况下,可以省略配置
class Springboot15SqlApplicationTests {
@Autowired
private BookDao bookDao;
@Test
void contextLoads() {
Book book = bookDao.selectById(1);
System.out.println(book);
}
/**
* 非实体类封装数据的查询操作
*
* @param jdbcTemplate
*/
@Test
void testJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate) {
String sql = "select * from tbl_book";
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
System.out.println(maps);
}
/**
* 实体类封装数据的查询操作
*
* @param jdbcTemplate
*/
@Test
void testJdbcTemplate1(@Autowired JdbcTemplate jdbcTemplate) {
String sql = "select * from tbl_book";
RowMapper<Book> rm = new RowMapper<Book>() {
@Override
public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
Book temp = new Book();
temp.setId(rs.getInt("id"));
temp.setName(rs.getString("name"));
temp.setType(rs.getString("type"));
temp.setDescription(rs.getString("description"));
return temp;
}
};
List<Book> list = jdbcTemplate.query(sql, rm);
System.out.println(list);
}
@Test
void testJdbcTemplateSave(@Autowired JdbcTemplate jdbcTemplate) {
String sql = "insert into tbl_book values(3,'springboot1','springboot2','springboot3')";
jdbcTemplate.update(sql);
}
}
# h2 数据库
server:
port: 80
spring:
h2:
console: # 控制台模式
enabled: true # 设置为true表示开启
path: /h2 # 访问路径
datasource:
url: jdbc:h2:~/test
hikari:
# driver-class-name: org.h2.Driver
username: sa
password: 123456
小结
H2内嵌式数据库启动方式,添加坐标,添加配置H2数据库线上运行时请务必关闭总结
数据源配置(Hikari)持久化技术(JdbcTemplate)数据库(H2)
版权声明
本文仅代表作者观点,不代表博信信息网立场。