SpringBoot中单元测试的使用

pom文件需要引入的坐标

<!--        test的起步依赖-->
<dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
</dependency>

需要使用的注解

import org.elasticsearch.client.RestHighLevelClient;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {YourApplication.class})
public class EsDemoTest {
    @Autowired
    private XXXXXService xxxxxService;

    @Test
    public void test(){
        System.out.println(xxxxxService.xxx);
    }

}

出现的问题

会报错,我是报下面的错误

 [o.s.test.context.TestContextManager ]: Caught exception while allowing TestExecutionListener
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 

问题解决

我的是因为之前的一个配置文件没有修改所以才出现的问题,我看网上的解决方法有下面的几种

  1. spring-test坐标的版本较低,按我上面的pom坐标这么改便可以解决问题(这个是我第一次报错的时候改的),参考博文
  2. 在@SpringBootTest这个注解上面增加一个参数,如下所示:参考博客,Web项目
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    
    @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) 作用是启动Spring的ApplicationContext,参数webEnvironment指定了运行的web环境

注解

注解与 Junit4 是一致的
在这里插入图片描述
更多的配置说明可以看:这篇博客

@RunWith(SpringRunner.class)

是JUnit的注解,作用是关联Spring Boot Test,使运行JUnit时同时启动Spring,该注解能够让Junit运行在Spring测试环境中,得到Spring上下文支持
4.3版本中提供了等同于SpringJUnit4ClassRunner.class的简写类SpringRunner.class

@SpringBootTest

当测试中需要使用Spring Boot功能时,可以使用@SpringBootTest注解,装配Spring上下文。

@SpringBootTest注解意思就是将SpringBoot主类中导入的bean全都包含进来。

当作为普通Spring单元测试时,可以使用@ContextConfiguration,装配Spring 上下文。

如果指定的classes为普通Configer(@SpringBootConfiguration)配置类,则会以此配置初始化Spring 上下文,而不会加载其他Bean到Spring容器

如果未指定classes 参数或者指定的classes参数不是启动main函数入口@SpringBootTest(classes = {YourApplication.class})(也就是使用@SpringBootApplication注解的类),则会自动从当前测试类包一层一层向上检索,直到找到有@SpringBootApplication或@SpringBootConfiguration注释的类为止。以此来启动Spring Boot应用,并装载Spring上下文。

如果未检索到Spring Boot启动主配置类,则会抛出异常: java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=…) with your test

其包含的属性如下:

  1. value 指定配置属性
  2. properties 指定配置属性,和value意义相同
  3. classes 指定配置类,等同于@ContextConfiguration中的class,若没有显示指定,将查找嵌套的@Configuration类,然后返回到SpringBootConfiguration搜索配置
  4. webEnvironment 指定web环境,可选值有:MOCK、RANDOM_PORT、DEFINED_PORT、NONE
    • MOCK 此值为默认值,该类型提供一个mock环境,此时内嵌的服务(servlet容器)并没有真正启动,也不会监听web端口。
    • RANDOM_PORT 启动一个真实的web服务,监听一个随机端口。
    • DEFINED_PORT 启动一个真实的web服务,监听一个定义好的端口(从配置中读取)。
    • NONE 启动一个非web的ApplicationContext,既不提供mock环境,也不提供真是的web服务。

集成测试侧重于集成应用程序的不同层,意味着不涉及Mock

@ContextConfiguration

@ContextConfiguration这个注解通常与@RunWith(SpringJUnit4ClassRunner.class)联合使用用来测试

4.3版本中提供了等同于SpringJUnit4ClassRunner.class的简写类SpringRunner.class

当作为普通Spring单元测试时,可以使用@ContextConfiguration,装配Spring 上下文。

当一个类添加了注解@Component,那么他就自动变成了一个bean

当这些bean收集起来之后,当我们想要在某个测试类使用@Autowired注解来引入这些收集起来的bean时,只需要给这个测试类添加@ContextConfiguration注解来标注我们想要导入这个测试类的某些bean

使用

@Component
public class CDPlayConfig {
 
}

进行测试,这个只会将这个Config注入的ben获取到

@RunWith(SpringRunner.class)
@ContextConfiguration(classes=CDPlayConfig.class)
public class CDPlayerTest {
 	@Autowired
 	private CDPlayConfig Config;
	@Test
	public void test(){
		congiog.xxx;
	}
	
}

多bean导入

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {A.class, B.class})
public class CDPlayerTest {

}

如果默认不写的话,可以根据测试的类名,去找到与之对应的配置文件。

可以查看这篇博客

@TestConfiguration

通过@TestConfiguration创建的Bean,作用范围在test里,不会影响到正式环境。通过@Autowired可以拿到创建的bean:
这一篇参考的博文代码有一点问题,下面的是我自己本地测试过的,没有写得大奖还是谨慎参考吧,这个博客后面的那些测试方法测试过,发现行不通,可能是pom坐标问题吧

@TestConfiguration作为内部类

通过@TestConfiguration创建的Bean,作用范围在test里,不会影响到正式环境。通过@Autowired可以拿到创建的bean:
只能是同一个类里面,如果在其他类里面进行测试,使用@Autowired注入后,是会报错端的,需要不报错需要使用@Import把配置类导入进来,或者也可使用@SpringBootTest(classes = CourseConfig.class)导入配置类

@SpringBootTest
public class CourseInnerClassTest {

    @TestConfiguration
    static class CourseConfig {
    	//这里需要使用static,不使用的话是会报错的
        @Bean
        public static String course() {
            returnTestConfiguration;
        }
    }

    @Autowired
    private Course course;

    @Test
    public void test() {
    	//断言,判断返回的值是否正确,正确的话是不会有如何报错的,对比content的内容和"TestConfiguration"是否一致,不一致的话会报错
        Assert.assertEquals(TestConfiguration, course);
    }
}

@TestConfiguration作为独立的配置类

@TestConfiguration
public class CourseConfig {
    @Bean
    public String course2() {
        return "TestConfiguration";
    }
}

在测试时,需要使用@Import把配置类导入进来,或者也可使用@SpringBootTest(classes = CourseConfig.class)导入配置类

@SpringBootTest
@Import(CourseConfig.class)
public class CourseSeparateClassTest {
    @Autowired
    private Course course2;

    @Test
    public void test() {
        Assert.assertEquals("TestConfiguration", CourseConfig.course2);
    }
}


@WebAppConfiguration

若你在一个测试类上加了@ContextConfiguration注解,则表示告诉Spring该集成测试加载的ApplicationContext应该是WebApplicationContext,表明这是一个WEB应用配置

作用是模拟ServletContext。注解在类上,用来声明加载的ApplicationContex 是一个WebApplicationContext ,它的属性指定的是Web资源的位置,默认为 src/main/webapp ,自定义修改为 resource

是一个用于声明集成测试所加载的ApplicationContext须是WebApplicationContext的类级别的注解

测试

超时测试

@Test(timeout = 1000)
public void testTimeout() throws InterruptedException {
    TimeUnit.SECONDS.sleep(2);
    System.out.println("Complete");
}

上面测试会失败,在一秒后会抛出异常org.junit.runners.model.TestTimeOutException:test timedout after 1000 millseconds

异常测试

可以测试代码是否它抛出想要得到的异常。expected参数和@Test 注释一起使用。现在让我们看看活动中的@Test(expected)

@Test(expected)
public void testTimeout() throws InterruptedException {
    TimeUnit.SECONDS.sleep(2);
    System.out.println("Complete");
}

mock

参考博文

参考博客2

参考博文3

在面向对象的程序设计中,模拟对象(mock object)是以可控的方式模拟真实对象行为的假对象。在编程过程中,通常通过模拟一些输入数据,来验证程序是否达到预期结果。

使用模拟对象,可以模拟复杂的、真实的对象行为。如果在单元测试中无法使用真实对象,可采用模拟对象进行替代。

使用Mockito一般分为三个步骤:

1.模拟测试类所需的外部依赖
2.执行测试代码
3.判断执行结果是否达到预期

Mockito 是一种 Java Mock 框架,主要是用来做 Mock 测试,它可以模拟任何 Spring 管理的 Bean、模拟方法的返回值、模拟抛出异常等等,避免你为了测试一个方法,却要自行构建整个 bean 的依赖链

同时也会记录调用这些模拟方法的参数、调用顺序,从而可以校验出这个 Mock 对象是否有被正确的顺序调用,以及按照期望的参数被调用

在这里插入图片描述

与Mockito使用–@MockBean

使用@MockBean注解来生成一个mock的bean,我们可以使用Mockito.when来模拟一些方法(比如Mock Jpa的Repository的find方法,这样就算数据库里的数据还没有准备好,我们也可以自己模拟数据)
参考博文
使用

Mockito.when( 对象.方法名() ).thenReturn( 自定义结果 )

@SpringBootTest
public class CourseServiceTest {
    @MockBean
    private CourseService courseService;

    @Test
    public void test() {
        Mockito.when( 对象.方法名() ).thenReturn(自定义结果);
        Assert.assertEquals(自定义结果, 对象.方法名());
    }
}

Service单元测试

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {YourApplication.class})
public class EsDemoTest {
    @Autowired
    private XXXXXService xxxxxService;

    @Test
    public void test(){
        System.out.println(xxxxxService.xxx);
    }

}

Controller单元测试

参考博客
有时候需要对Controller层(API)做测试,这时候就得用到MockMvc,不必启动工程就能测试这些接口。

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

测试方法1:步骤

在boot项目中写一个controller接口,如下:

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

写测试方法,如下:

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
 
//SpringBoot1.4版本之前用的是SpringJUnit4ClassRunner.class
@RunWith(SpringRunner.class)
//SpringBoot1.4版本之前用的是@SpringApplicationConfiguration(classes = Application.class)
@SpringBootTest(classes = App.class)
//测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的
@WebAppConfiguration
public class HelloControllerTest {
 
    @Autowired
    private WebApplicationContext webApplicationContext;
    private MockMvc mockMvc;
 
    @Before
    public void setUp(){
        //MockMvcBuilders.webAppContextSetup(WebApplicationContext context):指定WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc;
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();//建议使用这种
    }
 
    @Test
    public void getHello() throws Exception{
 
        /**
         * 1、mockMvc.perform执行一个请求,Post请求就用.post方法
         * 2、MockMvcRequestBuilders.get("XXX")构造一个请求,Post请求就用.post方法
         * 3、ResultActions.param添加请求传值
         * 4、ResultActions.accept(MediaType.TEXT_HTML_VALUE))设置返回类型
        	accept(MediaType.APPLICATION_JSON_UTF8)代表客户端希望接受的数据类型为application/json;charset=UTF-8
        	contentType(MediaType.APPLICATION_JSON_UTF8)代表发送端发送的数据格式是application/json;charset=UTF-8
         * 5、ResultActions.andExpect添加执行完成后的断言。
         * 6、ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情
         	比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
         * 5、ResultActions.andReturn表示执行完成后,返回响应的结果。
         	比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息
         */
 
        MvcResult mvcResult= mockMvc.perform(MockMvcRequestBuilders.get("/testMock")
                .param("name","testmock")
                .accept(MediaType.TEXT_HTML_VALUE)
				//.contentType(MediaType.APPLICATION_JSON_UTF8)
				)
                //等同于Assert.assertEquals(200,status);
               // .andExpect(MockMvcResultMatchers.status().isOk())  
               //等同于 Assert.assertEquals("hello testmock",content);           
               // .andExpect(MockMvcResultMatchers.content().string("hello testmock"))    
                .andDo(MockMvcResultHandlers.print())
                .andReturn();
        int status=mvcResult.getResponse().getStatus();                 //得到返回代码
        String content=mvcResult.getResponse().getContentAsString();    //得到返回结果
 
		//断言,判断返回代码是否正确
        Assert.assertEquals(200,status);                      
        //断言,判断返回的值是否正确,对比content的内容和"hello ddd"是否一致,不一致的话会报错
        Assert.assertEquals("hello ddd",content);  
    }
}

然后直接测试即可

补充

MockMvcBuilders提供了对应的创建方法standaloneSetup 方法和webAppContextSetup方法,在使用时直接调用即可

private MockMvc mockMvc;

@Autowire
private WebApplicationContext webApplicationContext;

@Before
public void setup() {
    /*实例化方式一*/
    mockMvc = MockMvcBuilders.standaloneSetup(new UserController()).build();
    /*实例化方式二*/
    mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}

更多的测试可以看这个博客的Mockito章节

assertThat 基本语法
参考这篇博客

assertThat( [value], [matcher statement] );
  1. value 是接下来想要测试的变量值;
  2. matcher statement 是使用 Hamcrest 匹配符来表达的对前面变量所期望的值的声明,如果 value 值与 matcher statement 所表达的期望值相符,则测试成功,否则测试失败。
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

?abc!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值