详解Spring Boot Junit单元测试
junit这种老技术,现在又拿出来说,不为别的,某种程度上来说,更是为了要说明它在项目中的重要性。
凭本人的感觉和经验来说,在项目中完全按标准都写junit用例覆盖大部分业务代码的,应该不会超过一半。
刚好前段时间写了一些关于springboot的帖子,正好现在把junit再拿出来从几个方面再说一下,也算是给一些新手参考了。
那么先简单说一下为什么要写测试用例
1. 可以避免测试点的遗漏,为了更好的进行测试,可以提高测试效率
2. 可以自动测试,可以在项目打包前进行测试校验
3. 可以及时发现因为修改代码导致新的问题的出现,并及时解决
那么本文从以下几点来说明怎么使用junit,junit4比3要方便很多,细节大家可以自己了解下,主要就是版本4中对方法命名格式不再有要求,不再需要继承testcase,一切都基于注解实现。
1、springboot web项目中中如何使用junit
创建一个普通的java类,在junit4中不再需要继承testcase类了。
因为我们是web项目,所以在创建的java类中添加注解:
@runwith(springjunit4classrunner.class) // springjunit支持,由此引入spring-test框架支持! @springapplicationconfiguration(classes = springbootsampleapplication.class) // 指定我们springboot工程的application启动类 @webappconfiguration // 由于是web项目,junit需要模拟servletcontext,因此我们需要给我们的测试类加上@webappconfiguration。
接下来就可以编写测试方法了,测试方法使用@test注解标注即可。
在该类中我们可以像平常开发一样,直接@autowired来注入我们要测试的类实例。
下面是完整代码:
package org.springboot.sample; import static org.junit.assert.assertarrayequals; import org.junit.test; import org.junit.runner.runwith; import org.springboot.sample.service.studentservice; import org.springframework.beans.factory.annotation.autowired; import org.springframework.boot.test.springapplicationconfiguration; import org.springframework.test.context.junit4.springjunit4classrunner; import org.springframework.test.context.web.webappconfiguration; /** * * @author 单红宇(365384722) * @create 2016年2月23日 */ @runwith(springjunit4classrunner.class) @springapplicationconfiguration(classes = springbootsampleapplication.class) @webappconfiguration public class studenttest { @autowired private studentservice studentservice; @test public void likename() { assertarrayequals( new object[]{ studentservice.likename("小明2").size() > 0, studentservice.likename("坏").size() > 0, studentservice.likename("莉莉").size() > 0 }, new object[]{ true, false, true } ); // asserttrue(studentservice.likename("小明2").size() > 0); } }
接下来,你需要新增无数个测试类,编写无数个测试方法来保障我们开发完的程序的有效性。
2、junit基本注解介绍
//在所有测试方法前执行一次,一般在其中写上整体初始化的代码 @beforeclass //在所有测试方法后执行一次,一般在其中写上销毁和释放资源的代码 @afterclass //在每个测试方法前执行,一般用来初始化方法(比如我们在测试别的方法时,类中与其他测试方法共享的值已经被改变,为了保证测试结果的有效性,我们会在@before注解的方法中重置数据) @before //在每个测试方法后执行,在方法执行完成后要做的事情 @after // 测试方法执行超过1000毫秒后算超时,测试将失败 @test(timeout = 1000) // 测试方法期望得到的异常类,如果方法执行没有抛出指定的异常,则测试失败 @test(expected = exception.class) // 执行测试时将忽略掉此方法,如果用于修饰类,则忽略整个类 @ignore(“not ready yet”) @test @runwith
在junit中有很多个runner,他们负责调用你的测试代码,每一个runner都有各自的特殊功能,你要根据需要选择不同的runner来运行你的测试代码。
如果我们只是简单的做普通java测试,不涉及spring web项目,你可以省略@runwith注解,这样系统会自动使用默认runner来运行你的代码。
3、参数化测试
junit为我们提供的参数化测试需要使用 @runwith(parameterized.class)
然而因为junit 使用@runwith指定一个runner,在我们更多情况下需要使用@runwith(springjunit4classrunner.class)来测试我们的spring工程方法,所以我们使用assertarrayequals 来对方法进行多种可能性测试便可。
下面是关于参数化测试的一个简单例子:
package org.springboot.sample; import static org.junit.assert.asserttrue; import java.util.arrays; import java.util.collection; import org.junit.test; import org.junit.runner.runwith; import org.junit.runners.parameterized; import org.junit.runners.parameterized.parameters; @runwith(parameterized.class) public class parametertest { private string name; private boolean result; /** * 该构造方法的参数与下面@parameters注解的方法中的object数组中值的顺序对应 * @param name * @param result */ public parametertest(string name, boolean result) { super(); this.name = name; this.result = result; } @test public void test() { asserttrue(name.contains("小") == result); } /** * 该方法返回collection * * @return * @author shanhy * @create 2016年2月26日 */ @parameters public static collection<?> data(){ // object 数组中值的顺序注意要和上面的构造方法parametertest的参数对应 return arrays.aslist(new object[][]{ {"小明2", true}, {"坏", false}, {"莉莉", false}, }); } }
4、打包测试
正常情况下我们写了5个测试类,我们需要一个一个执行。
打包测试,就是新增一个类,然后将我们写好的其他测试类配置在一起,然后直接运行这个类就达到同时运行其他几个测试的目的。
代码如下:
@runwith(suite.class) @suiteclasses({atest.class, btest.class, ctest.class}) public class abcsuite { // 类中不需要编写代码 }
5、使用junit测试http的api接口
我们可以直接使用这个来测试我们的rest api,如果内部单元测试要求不是很严格,我们保证对外的api进行完全测试即可,因为api会调用内部的很多方法,姑且把它当做是整合测试吧。
下面是一个简单的例子:
package org.springboot.sample; import static org.junit.assert.assertnotnull; import static org.junit.assert.assertthat; import static org.junit.assert.asserttrue; import java.util.regex.matcher; import java.util.regex.pattern; import org.hamcrest.matchers; import org.junit.after; import org.junit.afterclass; import org.junit.before; import org.junit.beforeclass; import org.junit.ignore; import org.junit.test; import org.junit.runner.runwith; import org.springframework.beans.factory.annotation.value; import org.springframework.boot.test.springapplicationconfiguration; import org.springframework.boot.test.testresttemplate; import org.springframework.boot.test.webintegrationtest; import org.springframework.test.context.junit4.springjunit4classrunner; import org.springframework.util.linkedmultivaluemap; import org.springframework.util.multivaluemap; import org.springframework.web.client.resttemplate; /** * * @author 单红宇(365384722) * @create 2016年2月23日 */ @runwith(springjunit4classrunner.class) @springapplicationconfiguration(classes = springbootsampleapplication.class) //@webappconfiguration // 使用@webintegrationtest注解需要将@webappconfiguration注释掉 @webintegrationtest("server.port:0")// 使用0表示端口号随机,也可以具体指定如8888这样的固定端口 public class hellocontrollertest { private string datereg; private pattern pattern; private resttemplate template = new testresttemplate(); @value("${local.server.port}")// 注入端口号 private int port; @test public void test3(){ string url = "http://localhost:"+port+"/myspringboot/hello/info"; multivaluemap<string, object> map = new linkedmultivaluemap<string, object>(); map.add("name", "tom"); map.add("name1", "lily"); string result = template.postforobject(url, map, string.class); system.out.println(result); assertnotnull(result); assertthat(result, matchers.containsstring("tom")); } }
6、捕获输出
使用 outputcapture 来捕获指定方法开始执行以后的所有输出,包括system.out输出和log日志。
outputcapture 需要使用@rule注解,并且实例化的对象需要使用public修饰,如下代码:
@runwith(springjunit4classrunner.class) @springapplicationconfiguration(classes = springbootsampleapplication.class) //@webappconfiguration // 使用@webintegrationtest注解需要将@webappconfiguration注释掉 @webintegrationtest("server.port:0")// 使用0表示端口号随机,也可以具体指定如8888这样的固定端口 public class hellocontrollertest { @value("${local.server.port}")// 注入端口号 private int port; private static final logger logger = loggerfactory.getlogger(studentcontroller.class); @rule // 这里注意,使用@rule注解必须要用public public outputcapture capture = new outputcapture(); @test public void test4(){ system.out.println("helloworld"); logger.info("logo日志也会被capture捕获测试输出"); assertthat(capture.tostring(), matchers.containsstring("world")); } }
关于assert类中的一些断言方法,都很简单,本文不再赘述。
但是在新版的junit中,assertequals 方法已经被废弃,它建议我们使用assertarrayequals,旨在让我们测试一个方法的时候多传几种参数进行多种可能性测试。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: C4D怎么设计DNA双螺旋图形?
下一篇: 一个mysql死锁场景实例分析