SpringBoot_Junit
来源:互联网 发布:空号检测软件 编辑:程序博客网 时间:2024/06/17 05:53
前言
今天研究了下SpringBoot-Junit做测试,发现此类博客很多,唯一的缺憾就是有点旧,Springboot都升级到1.5.6.RELEASE(idea自动集成版本),咱们也该与时俱进嘛,故此有了下文。在此感谢BraveWangDev,本文是在此篇文章基础之上进行的版本升级。
pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.ideabook</groupId> <artifactId>springboot_junit</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springboot_junit</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- 测试模块,包括JUnit、Hamcrest、Mockito --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
(一)基础代码以及注解解释
@RunWith(SpringRunner.class) //引入Spring-test框架支持,查看源码发现SpringRunner继承SpringJUnit4ClassRunner@SpringBootTest(classes = MockServletConfig.class)public class JunitApplicationTest { private MockMvc mvc; /* 一、 MockServletContext 由于这是一个新建项目,只有一个helloWord路由,所以我们使用MockServletContext来测试 使用MockServletContext来构建一个空的WebApplicationContext,这样我们创建的HelloController 就可以在@Before函数中创建并传递到MockMvcBuilders.standaloneSetup()函数中。 */ @Before public void setUp() throws Exception{ mvc = MockMvcBuilders.standaloneSetup(new HelloController()).build(); } /* 二、注意 status() content() equalTo()三个方法 import static org.hamcrest.Matchers.equalTo; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; */ @Test public void getHello() throws Exception{ mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().isOk()) .andExpect(content().string(equalTo("hello!!!"))); } /* 三、test方法注解介绍 //所有测试方法执行前.执行一次,作用:整体初始化 @BeforeClass //所有测试方法完成后,执行一次,作用:销毁和释放资源 @AfterClass //每个测试方法前执行,作用:初始化方法 @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来运行你的代码。 */}
在测试这部分代码的时候,遇到一个有趣的问题:运行getHello()方法的时候,idea输出窗口 要么静悄悄啥都没有,要么红彤彤一片,还以为是自己抄写的代码不准确,囧,断言。。。
controller 代码demo
@RestControllerpublic class HelloController { @RequestMapping(value = "/hello",method = RequestMethod.GET) public String helloGet(){ return "hello!!!"; } @RequestMapping(value = "/hello",method = RequestMethod.POST) public String helloPost(String name){ return name + " ,hello!!!"; } @RequestMapping(value = "/hello",method = RequestMethod.PUT) public String helloPut(@RequestBody User user){ return user + " ,hello!!!"; } @RequestMapping(value = "/hello",method = RequestMethod.DELETE) public String helloDelete(@RequestBody User user){ return user + " ,hello!!!"; }}//restful风格API
(二)参数化测试
1. 断言介绍
/* Assert断言方法介绍 1、assertEquals 函数原型:assertEquals([String message],expected,actual) 参数说明: message是个可选的消息,假如提供,将会在发生错误时报告这个消息。 expected是期望值,通常都是用户指定的内容。 actual是被测试的代码返回的实际值。 例:assertEquals("equals","1","1"); */ private String getStr(){ return "asd"; } @Test public void testAssertEquals1(){ assertEquals("不相等","a",getStr()); assertEquals("不相等","as",getStr()); } /* 函数原型:assertEquals([String message],expected,actual,tolerance) 参数说明: message是个可选的消息,假如提供,将会在发生错误时报告这个消息。 expected是期望值,通常都是用户指定的内容。 actual是被测试的代码返回的实际值。 tolerance是误差参数,参加比较的两个浮点数在这个误差之内则会被认为是相等的。 */ @Test public void testAssertEquals2(){ assertEquals("你又不乖了。。。",3.33,10/3,0.04); } /* 2、assertTrue 函数原型:assertTrue ([String message],Boolean condition) 参数说明: message是个可选的消息,假如提供,将会在发生错误时报告这个消息。 condition是待验证的布尔型值。 该断言用来验证给定的布尔型值是否为真,假如结果为假,则验证失败。当然,更有验证为假的测试条件: 函数原型:assertFalse([String message],Boolean condition) 该断言用来验证给定的布尔型值是否为假,假如结果为真,则验证失败。 例: assertTrue("true",1==1); assertFalse("false",2==1); */ /* 3、assertNull 函数原型:assertNull([String message],Object object) 参数说明: message是个可选的消息,假如提供,将会在发生错误时报告这个消息。 object是待验证的对象。 该断言用来验证给定的对象是否为null,假如不为null,则验证失败。相应地,还存在能够验证非null的断言: 函数原型:assertNotNull([String message],Object object) 该断言用来验证给定的对象是否为非null,假如为null,则验证失败。 例:assertNull("null",null); assertNotNull("not null",new String()); */ @Test public void testAssertNull(){ //assertNull("null",null);// List list = new ArrayList();// assertNull("new ArrayList is not null" ,list); List list1 = Collections.emptyList(); assertNull("emptyList is not null",list1); } /* 4、assertSame 函数原型:assertSame ([String message], expected,actual) 参数说明: message是个可选的消息,假如提供,将会在发生错误时报告这个消息。 expected是期望值。 actual是被测试的代码返回的实际值。 该断言用来验证expected参数和actual参数所引用的是否是同一个对象,假如不是,则验证失败。 函数原型:assertNotSame ([String message], expected,actual) 该断言用来验证expected参数和actual参数所引用的是否是不同对象,假如所引用的对象相同,则验证失败。 例:assertSame("same",2,4-2); assertNotSame("not same",2,4-3); */ @Test public void testSame(){ //assertSame("same:这俩对象真不一样!!!",2,4-2); //assertSame("same:这俩对象真不一样!!!",2L,4-2); //assertSame("same:这俩对象真不一样!!!",new String("aaa"),new String("aaa")); //assertSame("same:这俩对象一样!!!","aaa","aaa"); //assertSame("same:这俩对象真不一样!!!","aaa",new String("aaa")); String a,b; a = "aaaaa"; b = a; assertSame("same:这俩对象一样!!!",a,b); assertSame("same:这俩对象一样!!!",a,"aaaaa"); } /* 5、fail 函数原型:fail([String message]) 参数说明: message是个可选的消息,假如提供,将会在发生错误时报告这个消息。 该断言会使测试立即失败,通常用在测试不能达到的分支上(如异常)。 测试方法是否进行完毕,如果没有则报错并中断,如下:i 的值不同,结果不同。 */ @Test public void testFail(){ for (int i = 0 ; i < 10 ; i ++){ if (i == 5){ fail("fail...."); } System.out.println(i); } } /* 6、assertArrayEquals 函数原型:assertArrayEquals([String message], Object[] expecteds,Object[] actuals) 参数说明: message是个可选的消息,假如提供,将会在发生错误时报告这个消息,以及不一致的具体信息(数组长度、首次出现不一致的地方..) expecteds 是多个期望值。 actuals是被测试的代码返回的实际值。 */ @Test public void testAssertArrayEquals(){ //assertArrayEquals("这俩数组不一样??",new Integer[]{1,3,5,7,9},new Integer[]{1,3,5}); /* java.lang.AssertionError: 这俩数组不一样??: array lengths differed, expected.length=5 actual.length=3 */ //assertArrayEquals("这俩数组不一样??",new Integer[]{1,3,5,7,9},new Integer[]{1,3,5,7,9}); //assertArrayEquals("这俩数组不一样??",new Integer[]{1,3,5,7,9},new Integer[]{2,4,6,8,10}); /*这俩数组不一样??: arrays first differed at element [0]; .... */ assertArrayEquals("这俩数组不一样??",new Integer[]{1,3,5,7,9},new Integer[]{1,3,6,8,10}); /* 这俩数组不一样??: arrays first differed at element [2]; Expected :5 Actual :6 */ }
(三)参数化测试
@RunWith(Parameterized.class)public class ParameterTest { private String name; private String password; //1.初始化测试参数 @Parameters public static Collection<?> data(){ System.out.println("===data==========="); return Arrays.asList(new Object[][]{ {"Test","123"}, {"ATest","12"}, {"BTest","123"}, }); } //2.将@Parameters注解的方法中的Object数组中值的顺序对应 public ParameterTest(String name, String password) { super(); System.out.println("===ParameterTest==================="); this.name = name; this.password = password; } //3.逻辑测试 /* import static org.junit.Assert.assertTrue; assertTrue():断言,如果结果为true,啥事没有 否则,就会弹出“java.lang.AssertionError” + 错误信息(红红的错误很醒目,还以为代码写错了,囧~~~) */ @Test public void test() { System.out.println("==test=========="); assertTrue(name.contains("Test")==true); assertTrue(password.equals("12")); }}
3.api测试
主要针对 get 、post方法的测试,而put、delete测试部分个人感觉 Junit支持的不够,限制太多。
测试的API对象, 仍然是HelloController。
@RunWith(SpringRunner.class)@SpringBootTest(webEnvironment=RANDOM_PORT)// 使用0表示端口号随机,也可以指定端口public class RestApiTest { private String dateReg; private Pattern pattern; private RestTemplate template = new TestRestTemplate().getRestTemplate(); @Value("${local.server.port}")// 注入端口号 private int port; @Test public void testApi_Get() throws URISyntaxException { URI uri = new URI("http://tingapi.ting.baidu.com/v1/restserver/ting?" + "format=json%E6%88%96xml&calback=&from=webapp_music" + "&method=baidu.ting.billboard.billList&type=1&size=10&offset=0"); String result = template.getForObject(uri, String.class); System.err.println(result); } @Test public void testApi_Post() throws URISyntaxException { String url = "http://localhost:"+port+"/hello"; System.out.println(""); System.err.println("url = " + url); MultiValueMap<String, Object> map = new LinkedMultiValueMap<>(); map.add("name", "value1"); String result = template.postForObject(url, map, String.class); System.err.println(result); } //put测试方法,没有返回值,改用exchange //exchange 以json形式请求,所以要定义Header,不然会报错,说是不支持这种MediaType。 //而对应的Api方法如下,换了方式就很难测试成功,也是醉醉哒!! // @RequestMapping(value = "/hello",method = RequestMethod.PUT) // public String helloPut(@RequestBody User user){} @Test public void testApi_Put() throws Exception { String reqJsonStr = "{\"age\":\"200\",\"name\":\"zsssss\"}"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<String> entity = new HttpEntity<String>(reqJsonStr,headers); String url = "http://localhost:"+port+"/hello"; ResponseEntity<String> exchange = template.exchange(url, HttpMethod.PUT, entity, String.class); String body = exchange.getBody(); System.err.println(body); System.err.println(exchange); } @Test public void testApi_Delete() throws Exception { String reqJsonStr = "{\"age\":\"200\",\"name\":\"zsssss\"}"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<String> entity = new HttpEntity<String>(reqJsonStr,headers); String url = "http://localhost:"+port+"/hello"; ResponseEntity<String> exchange = template.exchange(url, HttpMethod.DELETE, entity, String.class); String body = exchange.getBody(); System.err.println(body); System.err.println(exchange); } // put delete 虽然说是测试通过了,但是限制忒多,感觉不实用。 // 如 entity的设置,貌似只能用json形式的参数。 其他形式试了很久也没成功,囧。。。。欢迎补充!!!}
(四)打包测试
import org.junit.runner.RunWith;import org.junit.runners.Suite;/** * @Author: lzh * @Description: 打包测试,就是新增一个类,将其他测试类配置在一起,运行这个类达到运行多个测试类的目的 * @Date: Created in 2017/8/10 14:20 */@RunWith(Suite.class)@Suite.SuiteClasses({JunitApplicationTest.class,JunitApplicationTest.class,SpringBootJunitApplicationTests.class})public class SuiteTest { // 类中不需要编写代码}
代码下载链接