Spring-Test
Spring/Spring Boot/JUnit 测试相关笔记
首先要分清几个概念:测试方法、测试类、测试集、测试运行器。
- 测试方法就是用 @Test 注解的一些函数。
- 测试类是包含一个或多个测试方法的一个 XxTest.java 文件
- 测试集是一个suite, 可能包含多个测试类。
- 测试运行器则决定了用什么方式偏好去运行这些测试集/类/方法。
Spring Boot 模块独立测试
Spring Boot 支持各模块单独隔离测试,例如web, db。单独测试各模块时不需要启动整个spring上下文,通过禁用一些spring boot自动配置来实现。
Spring Boot Test Slices Overview and Usage
https://rieckpil.de/spring-boot-test-slices-overview-and-usage/
@WebMvcTest 测试web层
@WebMvcTest
测试web层,controller层,不包括service层。
@WebMvcTest 注解主要用于controller层测试,只覆盖应用程序的controller层,HTTP请求和响应是Mock出来的,因此不会创建真正的连接。因此需要创建 MockMvc bean进行模拟接口调用。
如果Controller层对Service层中的其他bean有依赖关系,那么需要使用Mock提供所需的依赖项。
WebMvcTest要快得多,因为我们只加载了应用程序的一小部分。
@DataJpaTest 测试 JPA
@DataJpaTest
测试 jpa @Repository EntityManager TestEntityManager DataSource
@DataJpaTest
注解会禁用 spring boot 的其他自动配置,只保留 jpa 测试相关的。
默认情况下 @DataJpaTest
注解的测试类都是事务型的,测试方法结束后会回滚操作。
默认情况下 @DataJpaTest
会启动一个内存数据库,例如 H2 或 Derby,来代替其他数据库。配合使用 @AutoConfigureTestDatabase
注解来自动配置一个测试库。
如果想加载全部spring上下文,同时使用内存数据库,应该使用 @SpringBootTest
搭配 @AutoConfigureTestDatabase
注解来实现。
@DataJpaTest
注解的测试类中可以直接注入 TestEntityManager
来作为 EntityManager
使用。
如果想在 @DataJpaTest
外使用 TestEntityManager, 需要添加 @AutoConfigureTestEntityManager 注解
TestEntityManager NullPointerException
一开始注入的 TestEntityManager 一直是 null,后来加上 @RunWith(SpringRunner.class) 就好了
有 @DataJpaTest
而不启动 Spring 上下文时,可以直接注入 TestEntityManager 使用。
如果不使用 @DataJpaTest
而是启动Spring上下文的话,就没有 TestEntityManager 实例可注入了,需要改为注入 EntityManager
Test Your Spring Boot JPA Persistence Layer With @DataJpaTest
https://rieckpil.de/test-your-spring-boot-jpa-persistence-layer-with-datajpatest/
@JdbcTest 测试jdbc
@JdbcTest
测试jdbc
@DataMongoTest 测试mongo
@DataMongoTest
测试mongo
@DataMongoTest
注解会禁用 spring boot 的其他自动配置,只保留 mongo 测试相关的。
@JsonTest 测试json
@JsonTest
测试json序列化、反序列化
@RestClientTest 测试http客户端
@RestClientTest
测试http客户端
@SpringBootTest 测试springboot应用
@SpringBootTest
测试spring boot应用,各种service层。
@ActiveProfiles 指定profile
可以使用 @ActiveProfiles 注解指定 profile
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("test")
public class SomeTestClass {
//...
}
还可以传数组 @ActiveProfiles({"profile1", "profile2"})
@Transactional 测试数据自动回滚
可以实现再springboot中使用junit编写单元测试,并且测试结果不影响数据库。
@Transactional 表示该方法整体为一个事务,可以用在测试类上表示所有测试方法都回滚,或具体的 @Test 方法上。
@Rollback 表示事务执行完回滚,支持传入一个参数value,默认true即回滚,false不回滚。
springboot中junit回滚
https://www.jianshu.com/p/d9d0abf317c0
使用 MockMvc 测试 Spring MVC Controller
用到的注解:
@RunWith(SpringJUnit4ClassRunner.class): 表示使用Spring Test组件进行单元测试;
@WebAppConfiguration: 使用这个Annotate会在跑单元测试的时候真实的启动一个web服务,然后开始调用Controller的Rest API,待单元测试跑完之后再将web服务停掉;
@ContextConfiguration: 指定Bean的配置文件信息,可以有多种方式,这个例子使用的是文件路径形式,如果有多个配置文件,可以将括号中的信息配置为一个字符串数组来表示;controller,component等都是使用注解,需要注解指定spring的配置文件,扫描相应的配置,将类初始化等。
@TransactionConfiguration(transactionManager=”transactionManager”,defaultRollback=true)配置事务的回滚,对数据库的增删改都会回滚,便于测试用例的循环利用
为什么要进行事务回滚:
1、测试过程对数据库的操作,会产生脏数据,影响我们数据的正确性
2、不方便循环测试,即假如这次我们将一个记录删除了,下次就无法再进行这个Junit测试了,因为该记录已经删除,将会报错。
3、如果不使用事务回滚,我们需要在代码中显式的对我们的增删改数据库操作进行恢复,将多很多和测试无关的代码
测试类基类
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
//这个必须使用junit4.9以上才有
@RunWith(SpringJUnit4ClassRunner.class)
//单元测试的时候真实的开启一个web服务
@WebAppConfiguration
//配置事务的回滚,对数据库的增删改都会回滚,便于测试用例的循环利用
@TransactionConfiguration(transactionManager="transactionManager",defaultRollback=true)
@Transactional
@ContextConfiguration(locations = {"classpath:spring.xml","classpath:spring-hibernate.xml"})
public class AbstractContextControllerTests {
@Autowired
protected WebApplicationContext wac;
}
具体测试类
package com.pengtu.gsj;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.hibernate.SessionFactory;
import org.junit.Before;
import org.junit.Test;
import org.owasp.esapi.ESAPI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.pengtu.gsj.controller.BannerController;
import com.pengtu.gsj.dao.UserDao;
import com.pengtu.gsj.entity.app.User;
import com.pengtu.gsj.service.UserService;
public class EsapiTest extends AbstractContextControllerTests{
private MockMvc mockMvc;
//该方法在每个方法执行之前都会执行一遍
@Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(new BannerController()).build();
}
/**
* perform:执行一个RequestBuilder请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理;
* get:声明发送一个get请求的方法。MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables):根据uri模板 和uri变量值得到一个GET请求方式的。另外提供了其他的请求的方法,如:post、put、delete等。
* param:添加request的参数,如上面发送请求的时候带上了了pcode = root的参数。假如使用需要发送json数据格式的时将不能使用这种 方式,可见后面被@ResponseBody注解参数的解决方法
* andExpect:添加ResultMatcher验证规则,验证控制器执行完成后结果是否正确(对返回的数据进行的判断);
* andDo:添加ResultHandler结果处理器,比如调试时打印结果到控制台(对返回的数据进行的判断);
* andReturn:最后返回相应的MvcResult;然后进行自定义验证/进行下一步的异步处理(对返回的数据进行的判断)
* @throws Exception
*/
@Test
public void getAllBanners() throws Exception{
String responseString = mockMvc.perform(get("/banner/hello") //请求的url,请求的方法是get
.contentType(MediaType.APPLICATION_JSON) //数据的格式
.param("id","123456789") //添加参数
).andExpect(status().isOk()) //返回的状态是200
.andDo(print()) //打印出请求和相应的内容
.andReturn().getResponse().getContentAsString(); //将相应的数据转换为字符串
System.out.println("--------返回的json = " + responseString);
}
}
perform:执行一个RequestBuilder请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理;
get:声明发送一个get请求的方法。MockHttpServletRequestBuilder get(String urlTemplate, Object… urlVariables):根据uri模板和uri变量值得到一个GET请求方式的。另外提供了其他的请求的方法,如:post、put、delete等。
param:添加request的参数,如上面发送请求的时候带上了了pcode = root的参数。假如使用需要发送json数据格式的时将不能使用这种方式。
andExpect:添加ResultMatcher验证规则,验证控制器执行完成后结果是否正确(对返回的数据进行的判断);
andDo:添加ResultHandler结果处理器,比如调试时打印结果到控制台(对返回的数据进行的判断);
andReturn:最后返回相应的MvcResult;然后进行自定义验证/进行下一步的异步处理(对返回的数据进行的判断)
使用MockMvc测试Spring mvc Controller
https://blog.csdn.net/zhang289202241/article/details/62042842
SpringRunner 和 SpringJUnit4ClassRunner
SpringRunner 是 SpringJUnit4ClassRunner 的别名,两者没有任何区别
What is the difference between SpringJUnit4ClassRunner and SpringRunner
https://stackoverflow.com/questions/47446529/what-is-the-difference-between-springjunit4classrunner-and-springrunner
Class SpringRunner
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/junit4/SpringRunner.html
SpringBoot 单元测试
1、要让一个普通类变成一个单元测试类只需要在类名上加入 @SpringBootTest
和 @RunWith(SpringRunner.class)
两个注释即可。
2、在测试方法上加上 @Test
注释。
Spring Boot 单元测试详解+实战教程
https://www.cnblogs.com/javastack/p/9150408.html
@SpringBootTest
@SpringBootTest
为 springApplication 创建上下文并支持 SpringBoot 特性
@SpringBootTest
注解告诉 SpringBoot 去寻找一个主配置类(例如带有 @SpringBootApplication
的配置类),并使用它来启动 Spring 应用程序上下文。SpringBootTest 加载完整的应用程序并注入所有可能的bean,因此速度会很慢。
在这种情况下,不需要创建 MockMvc bean,可以直接通过 RestTemplate 进行请求测试(或者使用 TestRestTemplate )。
使用 @SpringBootTest 的 webEnvironment 属性定义运行环境:
Mock(默认): 加载 WebApplicationContext 并提供模拟的 web 环境 Servlet环境,使用此批注时,不会启动嵌入式服务器
RANDOM_PORT: 加载 WebServerApplicationContext 并提供真实的 web 环境,嵌入式服务器,监听端口是随机的
DEFINED_PORT: 加载 WebServerApplicationContext 并提供真实的 Web 环境,嵌入式服务器启动并监听定义的端口(来自 application.properties 或默认端口 8080)
NONE: 使用 SpringApplication 加载 ApplicationContext 但不提供任何Web环境
Spring Test单元测试
http://jianwl.com/2016/08/07/Spring-Test%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/
Annotation Type SpringBootTest
https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/context/SpringBootTest.html
Unable to find a @SpringBootConfiguration
@SpringBootTest
注解的测试类执行报错:
java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test
Suppressed: java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test at org.springframework.util.Assert.state(Assert.java:73)
原因:@SpringBootTest
注解的测试类找不到 @SpringBootConfiguration
应用主类(测试主类 和 src/main/java 应用主类都找不到)
解决:
方法1、Spring 会先在当前目录找 @SpringBootConfiguration
应用主类,然后根据包目录结构向上级依次查找。
出问题的目录结构有问题,应用主类在 com.masikkk 包中,但测试类没放到任何包中,只在 src/test/java 中,将测试类放到和应用主类相同的包或子包中即可
注意:没必要创建一个测试主类 TestApplication,测试类能找到 src/main/java 同包中的主类即可
@SpringBootApplication
public class TestApplication {
}
方法2、如果确实无法移动测试类的包结构,可以通过 @SpringBootTest(classes = MyWebApplication.class)
的方式告诉测试类去哪里找应用主类。
@TestPropertySource 设置System Property
@TestPropertySource 可以用来覆盖掉来自于系统环境变量、Java系统属性、@PropertySource的属性。
同时@TestPropertySource(properties=…)优先级高于@TestPropertySource(locations=…)。
利用它我们可以很方便的在测试代码里微调、模拟配置(比如修改操作系统目录分隔符、数据源等)。
Spring、Spring Boot和TestNG测试指南 - @TestPropertySource
https://segmentfault.com/a/1190000010854607
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:whereever/context.xml")
@TestPropertySource(properties = {"myproperty = foo"})
public class TestWarSpringContext {
...
}
How to set environment variable or system property in spring tests?
https://stackoverflow.com/questions/11306951/how-to-set-environment-variable-or-system-property-in-spring-tests
@ClassRule 和 @Rule
junit中的 @ClassRule,可以在所有类方法开始前进行一些初始化调用,比如创建临时文件,
package com.jdriven;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
public class JUnitClassRuleTest {
@ClassRule
public static TemporaryFolder temporaryFolder = new TemporaryFolder();
public static File tempFile;
@BeforeClass
public static void createTempFile() throws IOException {
tempFile = temporaryFolder.newFile("tempFile.txt");
}
@Test
public void testJUnitClassRule_One() {
//Your test should go here, which uses tempFile
}
@Test
public void testJUnitClassRule_Two() {
//Your test should go here and uses the same tempFile
}
}
其中,@ClassRule中指定创建临时文件夹,这是在所有的测试方法前会创建文件夹,并且会在所有测试完成后,递归删除其下的子目录和子文件夹。
@Rule 是方法级别的,每个测试方法执行时都会调用被注解的Rule,而@ClassRule是类级别的,在执行一个测试类的时候只会调用一次被注解的Rule
junit中的@classrule,@rule
http://jackyrong.iteye.com/blog/2193451
@RunWith
@RunWith
指定JUnit使用的单元测试执行类,使用@RunWith注解可以改变JUnit的默认执行类
@Runwith放在测试类名之前,用来确定这个类怎么运行的。也可以不标注,会使用默认运行器。
常见的运行器有:
参数化运行器
@RunWith(Parameterized.class) 参数化运行器,配合@Parameters使用junit的参数化功能测试集运行器
@RunWith(Suite.class)
@SuiteClasses({ATest.class,BTest.class,CTest.class})
测试集运行器配合使用测试集功能junit4的默认运行器
@RunWith(JUnit4.class)
junit4的默认运行器兼容junit3.8的运行器
@RunWith(JUnit38ClassRunner.class)
用于兼容junit3.8的运行器SpringJUnit4ClassRunner
@RunWith(SpringJUnit4ClassRunner.class)集成了spring的一些功能
junit常用注解详细说明
http://www.cnblogs.com/tobey/p/4837495.html
使用RunWith注解改变JUnit的默认执行类,并实现自已的Listener
http://blog.csdn.net/fenglibing/article/details/8584602
@Test
在junit3中,是通过对测试类和测试方法的命名来确定是否是测试,且所有的测试类必须继承junit的测试基类。在junit4中,定义一个 测试方法变得简单很多,只需要在方法前加上@Test就行了。
注意:测试方法必须是public void,即公共、无返回数据。可以抛出异常。
junit常用注解详细说明
http://www.cnblogs.com/tobey/p/4837495.html
@ContextConfiguration
@ContextConfiguration
注解用于指定 spring 配置文件所在的路径,有以下几个常用的属性:
locations 配置文件路径
locations
可以通过该属性手工指定 Spring 配置文件所在的位置,可以指定一个或多个 Spring 配置文件。如下所示:
@ContextConfiguration(locations = {"xx/yy/beans1.xml","xx/yy/beans2.xml"})
@ContextConfiguration(locations = "classpath*:spring-ctx-*.xml")
inheritLocations 是否继承父类配置
inheritLocations
:是否要继承父测试用例类中的 Spring 配置文件,默认为 true。如下面的例子:
@ContextConfiguration(locations={"base-context.xml"})
public class BaseTest {
// ...
}
@ContextConfiguration(locations={"extended-context.xml"})
public class ExtendedTest extends BaseTest {
// ...
}
如果 inheritLocations 设置为 false,则 ExtendedTest 仅会使用 extended-context.xml 配置文件,否则将使用 base-context.xml 和 extended-context.xml 这两个配置文件。
classes 指定配置类
classes 属性可指定一些 Configuration 配置类,例如:
@SpringBootTest
@ContextConfiguration(classes = MyConfiguration.class)
public class ConsulLockTest {
}
Spring基于注解TestContext 测试框架使用详解
http://blog.csdn.net/yaerfeng/article/details/25368447
Spring 注解学习手札(六) 测试
http://snowolf.iteye.com/blog/588351
Spring单元测试禁用consul
问题:
本地不启动 consul 服务的情况下,跑单测报错连不上默认的 consul 地址 localhost:8500
2022-09-14 10:24:37.529 ERROR 12840 --- [ main] o.s.test.context.TestContextManager : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@6e9a5ed8] to prepare test instance [com.masikkk.MyJpaServiceTest@6c101cc1]
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132) ~[spring-test-5.3.8.jar:5.3.8]
Caused by: org.springframework.cloud.consul.config.ConsulPropertySources$PropertySourceNotFoundException: com.ecwid.consul.transport.TransportException: org.apache.http.conn.HttpHostConnectException: Connect to localhost:8500 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused (Connection refused)
at org.springframework.cloud.consul.config.ConsulPropertySources.createPropertySource(ConsulPropertySources.java:143) ~[spring-cloud-consul-config-3.0.4.jar:3.0.4]
Caused by: com.ecwid.consul.transport.TransportException: org.apache.http.conn.HttpHostConnectException: Connect to localhost:8500 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused (Connection refused)
at com.ecwid.consul.transport.AbstractHttpTransport.executeRequest(AbstractHttpTransport.java:83) ~[consul-api-1.4.5.jar:na]
Caused by: org.apache.http.conn.HttpHostConnectException: Connect to localhost:8500 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused (Connection refused)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:156) ~[httpclient-4.5.13.jar:4.5.13]
解决:
单测中禁用 consul
@TestPropertySource(properties = {"spring.cloud.consul.enabled=false"})
@SpringBootTest
public abstract class BaseTest {
}
https://stackoverflow.com/questions/39622363/spring-consul-disable-for-unit-tests
AopTestUtils.getUltimateTargetObject() 获取被代理的对象
上一篇 Nginx
页面信息
location:
protocol
: host
: hostname
: origin
: pathname
: href
: document:
referrer
: navigator:
platform
: userAgent
: