User-Profile-Image
hankin
  • 5
  • Java
  • Kotlin
  • Spring
  • Web
  • SQL
  • MegaData
  • More
  • Experience
  • Enamiĝu al vi
  • 分类
    • Zuul
    • Zookeeper
    • XML
    • WebSocket
    • Web Notes
    • Web
    • Vue
    • Thymeleaf
    • SQL Server
    • SQL Notes
    • SQL
    • SpringSecurity
    • SpringMVC
    • SpringJPA
    • SpringCloud
    • SpringBoot
    • Spring Notes
    • Spring
    • Servlet
    • Ribbon
    • Redis
    • RabbitMQ
    • Python
    • PostgreSQL
    • OAuth2
    • NOSQL
    • Netty
    • MySQL
    • MyBatis
    • More
    • MinIO
    • MegaData
    • Maven
    • LoadBalancer
    • Kotlin Notes
    • Kotlin
    • Kafka
    • jQuery
    • JavaScript
    • Java Notes
    • Java
    • Hystrix
    • Git
    • Gateway
    • Freemarker
    • Feign
    • Eureka
    • ElasticSearch
    • Docker
    • Consul
    • Ajax
    • ActiveMQ
  • 页面
    • 归档
    • 摘要
    • 杂图
    • 问题随笔
  • 友链
    • Spring Cloud Alibaba
    • Spring Cloud Alibaba - 指南
    • Spring Cloud
    • Nacos
    • Docker
    • ElasticSearch
    • Kotlin中文版
    • Kotlin易百
    • KotlinWeb3
    • KotlinNhooo
    • 前端开源搜索
    • Ktorm ORM
    • Ktorm-KSP
    • Ebean ORM
    • Maven
    • 江南一点雨
    • 江南国际站
    • 设计模式
    • 熊猫大佬
    • java学习
    • kotlin函数查询
    • Istio 服务网格
    • istio
    • Ktor 异步 Web 框架
    • PostGis
    • kuangstudy
    • 源码地图
    • it教程吧
    • Arthas-JVM调优
    • Electron
    • bugstack虫洞栈
    • github大佬宝典
    • Sa-Token
    • 前端技术胖
    • bennyhuo-Kt大佬
    • Rickiyang博客
    • 李大辉大佬博客
    • KOIN
    • SQLDelight
    • Exposed-Kt-ORM
    • Javalin—Web 框架
    • http4k—HTTP包
    • 爱威尔大佬
    • 小土豆
    • 小胖哥安全框架
    • 负雪明烛刷题
    • Kotlin-FP-Arrow
    • Lua参考手册
    • 美团文章
    • Java 全栈知识体系
    • 尼恩架构师学习
    • 现代 JavaScript 教程
    • GO相关文档
    • Go学习导航
    • GoCN社区
    • GO极客兔兔-案例
    • 讯飞星火GPT
    • Hollis博客
    • PostgreSQL德哥
    • 优质博客推荐
    • 半兽人大佬
    • 系列教程
    • PostgreSQL文章
    • 云原生资料库
    • 并发博客大佬
Help?

Please contact us on our email for need any support

Support
    首页   ›   Spring   ›   SpringBoot   ›   正文
SpringBoot

SpringBoot—单元测试(补充)

2020-03-28 02:41:52
1281  0 0
参考目录 隐藏
1) 总目录:
2) Service单元测试
3) Controller单元测试
4) 关于 MockMvc 的更多内容:
5) 用 MockMvc 来测试是第一种还可以用 RestTemplate 来测试 Controller :
6) 关于 RestTemplate 的更多内容:
7) 新断言assertThat使用
8) 关于旧断言的内容:
9) assertThat 的基本语法如下:
10) assertThat 的优点
11) 如何使用 assertThat
12) JSon测试
13) 单元测试回滚

阅读完需:约 12 分钟

这次来介绍下Spring Boot中对单元测试的整合使用,本篇会通过以下4点来介绍,基本满足日常需求:

  • Service层单元测试
  • Controller层单元测试
  • 新断言assertThat使用
  • 单元测试的回滚

总目录:

Service单元测试

创建一个HelloService文件

@Service
public class HelloService {
    public String sayHello(String name){
        return "hello "+name;
    }
}

测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class Junit2ApplicationTests {

    @Autowired
    HelloService helloService;
    @Test
    public void contextLoads() {
        String hello =helloService.sayHello("java");
        Assert.assertThat(hello, Matchers.is("hello java"));

    }
}

Controller单元测试

上面只是针对Service层做测试,但是有时候需要对Controller层(API)做测试,这时候就得用到MockMvc了,你可以不必启动工程就能测试这些接口。

MockMvc实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便。

创建一个HelloController 文件

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello(String name){
        return "hello "+name;
    }

    @PostMapping("/book")
    public  Book addbook(@RequestBody Book book){
        return  book;
    }
}

测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class Junit2ApplicationTests {
    
    @Autowired
    WebApplicationContext wac;
    MockMvc mockMvc;
    
    @Before
    public  void before(){
        mockMvc= MockMvcBuilders.webAppContextSetup(wac).build();
    }

    @Test
    public void test() throws Exception {
        MvcResult perform=mockMvc.perform(
                MockMvcRequestBuilders.get("/hello")
                        .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                        .param("name","java"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();
        System.out.println(perform.getResponse().getContentAsString());

    }

    //传一个json测试
    @Test
    public void test2() throws Exception {
        Book book=new Book();
        book.setId(12);
        book.setName("三号");
        book.setAuthor("上课");
        String s = new ObjectMapper().writeValueAsString(book);
        MvcResult mvcResult=mockMvc.perform(MockMvcRequestBuilders.post("/book").contentType(MediaType.APPLICATION_JSON).content(s))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andReturn();
        System.out.println(mvcResult.getResponse().getContentAsString());

    }

}

这里介绍一下MockMvc简单的方法 :

  1. mockMvc.perform执行一个请求
  2. MockMvcRequestBuilders.get(“/user/1”)构造一个请求,Post请求就用.post方法
  3. contentType(MediaType.APPLICATION_JSON_UTF8)代表发送端发送的数据格式是application/json;charset=UTF-8
  4. accept(MediaType.APPLICATION_JSON_UTF8)代表客户端希望接受的数据类型为application/json;charset=UTF-8
  5. session(session)注入一个session,这样拦截器才可以通过
  6. ResultActions.andExpect添加执行完成后的断言
  7. ResultActions.andExpect(MockMvcResultMatchers.status().isOk())方法看请求的状态响应码是否为200如果不是则抛异常,测试不通过
  8. andExpect(MockMvcResultMatchers.jsonPath(“$.author”).value(“嘟嘟MD独立博客”))这里jsonPath用来获取author字段比对是否为嘟嘟MD独立博客,不是就测试不通过
  9. ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息

关于 MockMvc 的更多内容:

SpringBoot—MockMvc单元测试

用 MockMvc 来测试是第一种还可以用 RestTemplate 来测试 Controller :

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class Junit2ApplicationTests2 {
    @Autowired
    TestRestTemplate testRestTemplate;
    @Test
    public void contextLoads() {
        String s=testRestTemplate.getForObject("/hello?name={1}",String.class,"java");
        System.out.println(s);
    }
}

关于 RestTemplate 的更多内容:

Spring—RestTemplate几种常见的请求方式

新断言assertThat使用

JUnit 4.4 结合 Hamcrest 提供了一个全新的断言语法——assertThat。程序员可以只使用 assertThat 一个断言语句,结合 Hamcrest 提供的匹配符,就可以表达全部的测试思想,我们引入的版本是Junit4.12所以支持assertThat。

关于旧断言的内容:

Spring—Assert断言测试(旧)(常用方法)

assertThat 的基本语法如下:

清单 1 assertThat 基本语法

assertThat( [value], [matcher statement] );
  • value 是接下来想要测试的变量值;
  • matcher statement 是使用 Hamcrest 匹配符来表达的对前面变量所期望的值的声明,如果 value 值与 matcher statement 所表达的期望值相符,则测试成功,否则测试失败。

assertThat 的优点

  • 优点 1:以前 JUnit 提供了很多的 assertion 语句,如:assertEquals,assertNotSame,assertFalse,assertTrue,assertNotNull,assertNull 等,现在有了 JUnit 4.4,一条 assertThat 即可以替代所有的 assertion 语句,这样可以在所有的单元测试中只使用一个断言方法,使得编写测试用例变得简单,代码风格变得统一,测试代码也更容易维护。
  • 优点 2:assertThat 使用了 Hamcrest 的 Matcher 匹配符,用户可以使用匹配符规定的匹配准则精确的指定一些想设定满足的条件,具有很强的易读性,而且使用起来更加灵活。如清单 2 所示:

清单 2 使用匹配符 Matcher 和不使用之间的比较

// 想判断某个字符串 s 是否含有子字符串 "developer" 或 "Works" 中间的一个
// JUnit 4.4 以前的版本:assertTrue(s.indexOf("developer")>-1||s.indexOf("Works")>-1 );
// JUnit 4.4:
assertThat(s, anyOf(containsString("developer"), containsString("Works"))); 
// 匹配符 anyOf 表示任何一个条件满足则成立,类似于逻辑或 "||", 匹配符 containsString 表示是否含有参数子 
// 字符串,文章接下来会对匹配符进行具体介绍 
  • 优点 3:assertThat 不再像 assertEquals 那样,使用比较难懂的“谓宾主”语法模式(如:assertEquals(3, x);),相反,assertThat 使用了类似于“主谓宾”的易读语法模式(如:assertThat(x,is(3));),使得代码更加直观、易读。
  • 优点 4:可以将这些 Matcher 匹配符联合起来灵活使用,达到更多目的。如清单 3 所示:

清单 3 Matcher 匹配符联合使用

// 联合匹配符not和equalTo表示“不等于”
assertThat( something, not( equalTo( "developer" ) ) ); 
// 联合匹配符not和containsString表示“不包含子字符串”
assertThat( something, not( containsString( "Works" ) ) ); 
// 联合匹配符anyOf和containsString表示“包含任何一个子字符串”
assertThat(something, anyOf(containsString("developer"), containsString("Works")));
  • 优点 5:错误信息更加易懂、可读且具有描述性(descriptive)
    JUnit 4.4 以前的版本默认出错后不会抛出额外提示信息,如:
 assertTrue( s.indexOf("developer") > -1 || s.indexOf("Works") > -1 );

如果该断言出错,只会抛出无用的错误信息,如:junit.framework.AssertionFailedError:null。
如果想在出错时想打印出一些有用的提示信息,必须得程序员另外手动写,如:

 assertTrue( "Expected a string containing 'developer' or 'Works'", 
    s.indexOf("developer") > -1 || s.indexOf("Works") > -1 );

非常的不方便,而且需要额外代码。
JUnit 4.4 会默认自动提供一些可读的描述信息,如清单 4 所示:
清单 4 JUnit 4.4 默认提供一些可读的描述性错误信息

 String s = "hello world!"; 
assertThat( s, anyOf( containsString("developer"), containsString("Works") ) ); 
// 如果出错后,系统会自动抛出以下提示信息:
java.lang.AssertionError: 
Expected: (a string containing "developer" or a string containing "Works") 
got: "hello world!" 

如何使用 assertThat

JUnit 4.4 自带了一些 Hamcrest 的匹配符 Matcher,但是只有有限的几个,在类 org.hamcrest.CoreMatchers 中定义,要想使用他们,必须导入包 org.hamcrest.CoreMatchers.*。

清单 5 列举了大部分 assertThat 的使用例子:

 字符相关匹配符
/**equalTo匹配符断言被测的testedValue等于expectedValue,
* equalTo可以断言数值之间,字符串之间和对象之间是否相等,相当于Object的equals方法
*/
assertThat(testedValue, equalTo(expectedValue));
/**equalToIgnoringCase匹配符断言被测的字符串testedString
*在忽略大小写的情况下等于expectedString
*/
assertThat(testedString, equalToIgnoringCase(expectedString));
/**equalToIgnoringWhiteSpace匹配符断言被测的字符串testedString
*在忽略头尾的任意个空格的情况下等于expectedString,
*注意:字符串中的空格不能被忽略
*/
assertThat(testedString, equalToIgnoringWhiteSpace(expectedString);
/**containsString匹配符断言被测的字符串testedString包含子字符串subString**/
assertThat(testedString, containsString(subString) );
/**endsWith匹配符断言被测的字符串testedString以子字符串suffix结尾*/
assertThat(testedString, endsWith(suffix));
/**startsWith匹配符断言被测的字符串testedString以子字符串prefix开始*/
assertThat(testedString, startsWith(prefix));
一般匹配符
/**nullValue()匹配符断言被测object的值为null*/
assertThat(object,nullValue());
/**notNullValue()匹配符断言被测object的值不为null*/
assertThat(object,notNullValue());
/**is匹配符断言被测的object等于后面给出匹配表达式*/
assertThat(testedString, is(equalTo(expectedValue)));
/**is匹配符简写应用之一,is(equalTo(x))的简写,断言testedValue等于expectedValue*/
assertThat(testedValue, is(expectedValue));
/**is匹配符简写应用之二,is(instanceOf(SomeClass.class))的简写,
*断言testedObject为Cheddar的实例
*/
assertThat(testedObject, is(Cheddar.class));
/**not匹配符和is匹配符正好相反,断言被测的object不等于后面给出的object*/
assertThat(testedString, not(expectedString));
/**allOf匹配符断言符合所有条件,相当于“与”(&&)*/
assertThat(testedNumber, allOf( greaterThan(8), lessThan(16) ) );
/**anyOf匹配符断言符合条件之一,相当于“或”(||)*/
assertThat(testedNumber, anyOf( greaterThan(16), lessThan(8) ) );
数值相关匹配符
/**closeTo匹配符断言被测的浮点型数testedDouble在20.0¡À0.5范围之内*/
assertThat(testedDouble, closeTo( 20.0, 0.5 ));
/**greaterThan匹配符断言被测的数值testedNumber大于16.0*/
assertThat(testedNumber, greaterThan(16.0));
/** lessThan匹配符断言被测的数值testedNumber小于16.0*/
assertThat(testedNumber, lessThan (16.0));
/** greaterThanOrEqualTo匹配符断言被测的数值testedNumber大于等于16.0*/
assertThat(testedNumber, greaterThanOrEqualTo (16.0));
/** lessThanOrEqualTo匹配符断言被测的testedNumber小于等于16.0*/
assertThat(testedNumber, lessThanOrEqualTo (16.0));
集合相关匹配符
/**hasEntry匹配符断言被测的Map对象mapObject含有一个键值为"key"对应元素值为"value"的Entry项*/
assertThat(mapObject, hasEntry("key", "value" ) );
/**hasItem匹配符表明被测的迭代对象iterableObject含有元素element项则测试通过*/
assertThat(iterableObject, hasItem (element));
/** hasKey匹配符断言被测的Map对象mapObject含有键值“key”*/
assertThat(mapObject, hasKey ("key"));
/** hasValue匹配符断言被测的Map对象mapObject含有元素值value*/
assertThat(mapObject, hasValue(value)); 

JSon测试

@RunWith(SpringRunner.class)
//@SpringBootTest
@JsonTest
public class JsonTests2 {
    @Autowired
    JacksonTester<Book> bookJacksonTester;
    @Test
    public void contextLoads() throws IOException {
        Book book=new Book();
        book.setId(99);
        book.setName("sad");
        book.setAuthor("擦拭");
        Assertions.assertThat(bookJacksonTester.write(book))
                .isEqualToJson("book.json");
        Assertions.assertThat(bookJacksonTester.write(book))
                .hasJsonPathStringValue("@.name");
        Assertions.assertThat(bookJacksonTester.write(book))
                .extractingJsonPathStringValue("@.name")
                .isEqualTo("sad");
    }

    @Test
    public  void test2() throws IOException {
        String content="{\"id\": \"12\", \"name\": \"asd\", \"author\": \"上课\"}";
        Assertions.assertThat(bookJacksonTester.parseObject(content).getName()).isEqualTo("asd");
    }
}

单元测试回滚

单元个测试的时候如果不想造成垃圾数据,可以开启事物功能,记在方法或者类头部添加@Transactional注解即可,如下:

@Test
@Transactional
public void add(){
    LearnResource bean = new LearnResource();
    bean.setAuthor("测试回滚");
    bean.setTitle("回滚用例");
    bean.setUrl("http://tengj.top");
    learnService.save(bean);
}

这样测试完数据就会回滚了,不会造成垃圾数据。如果你想关闭回滚,只要加上@Rollback(false)注解即可。@Rollback表示事务执行完回滚,支持传入一个参数value,默认true即回滚,false不回滚。

如果你使用的数据库是Mysql,有时候会发现加了注解@Transactional 也不会回滚,那么你就要查看一下你的默认引擎是不是InnoDB,如果不是就要改成InnoDB。

MyISAM与InnoDB是mysql目前比较常用的两个数据库存储引擎,MyISAM与InnoDB的主要的不同点在于性能和事务控制上。这里简单的介绍一下两者间的区别和转换方法:

  • MyISAM:MyISAM是MySQL5.5之前版本默认的数据库存储引擎。MYISAM提供高速存储和检索,以及全文搜索能力,适合数据仓库等查询频繁的应用。但不支持事务、也不支持外键。MyISAM格式的一个重要缺陷就是不能在表损坏后恢复数据。
  • InnoDB:InnoDB是MySQL5.5版本的默认数据库存储引擎,不过InnoDB已被Oracle收购,MySQL自行开发的新存储引擎Falcon将在MySQL6.0版本引进。InnoDB具有提交、回滚和崩溃恢复能力的事务安全。但是比起MyISAM存储引擎,InnoDB写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引。尽管如此,但是InnoDB包括了对事务处理和外来键的支持,这两点都是MyISAM引擎所没有的。
  • MyISAM适合:(1)做很多count 的计算;(2)插入不频繁,查询非常频繁;(3)没有事务。
  • InnoDB适合:(1)可靠性要求比较高,或者要求事务;(2)表更新和查询都相当的频繁,并且表锁定的机会比较大的情况。(4)性能较好的服务器,比如单独的数据库服务器,像阿里云的关系型数据库RDS就推荐使用InnoDB引擎。

如本文“对您有用”,欢迎随意打赏作者,让我们坚持创作!

0 打赏
Enamiĝu al vi
不要为明天忧虑.因为明天自有明天的忧虑.一天的难处一天当就够了。
543文章 68评论 294点赞 593971浏览

随机文章
Kotlin-类型进阶—类属性的延迟初始化(二十)
4年前
Spring—RestTemplate几种常见的请求方式
5年前
SpringBoot—启动时执行
3年前
Java—Future模式(数据库连接池)
5年前
SpringCloud—Hystrix(五)请求合并
5年前
博客统计
  • 日志总数:543 篇
  • 评论数目:68 条
  • 建站日期:2020-03-06
  • 运行天数:1927 天
  • 标签总数:23 个
  • 最后更新:2024-12-20
Copyright © 2025 网站备案号: 浙ICP备20017730号 身体没有灵魂是死的,信心没有行为也是死的。
主页
页面
  • 归档
  • 摘要
  • 杂图
  • 问题随笔
博主
Enamiĝu al vi
Enamiĝu al vi 管理员
To be, or not to be
543 文章 68 评论 593971 浏览
测试
测试
看板娘
赞赏作者

请通过微信、支付宝 APP 扫一扫

感谢您对作者的支持!

 支付宝 微信支付