Spring Boot测试

Spring Boot测试

Spring 提供的了一个test框架,用于系统的集成测试。在Spring Boot中,你可以通过spring-boot-starter-test 来启动测试框架。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Junit测试

进行简单单元测试而不需要Spring Boot特性,可以直接使用Junit编写测试单元。

@BeforeClass和@AfterClass在类被实例化前(构造方法执行前)就被调用了,而且只执行一次,通常用来初始化和关闭资源。

@Before和@After和在每个@Test执行前后都会被执行一次。

@Test标记一个方法为测试方法单元,被@Ignore标记的测试方法不会被执行。

JUnit4为了保证每个测试方法都是单元测试,是独立的互不影响。所以每个测试方法执行前都会重新实例化测试类。

Spring Boot测试

当测试中需要使用Spring Boot功能时,可以使用@SpringBootTest注解,装配Spring上下文。
当作为普通Spring单元测试时,可以使用@ContextConfiguration,装配Spring 上下文。
基于Spring 的Junit测试单元需要使用@RunWith(SpringJUnit4ClassRunner.class)注解,该注解能够让Junit运行在Spring测试环境中,得到Spring上下文支持。在4.3版本中提供了等同于SpringJUnit4ClassRunner.class的简写类SpringRunner.class。

Spring Boot测试单元启动流程如下:

  • 如果未指定classes 参数或者指定的classes参数不是启动main函数入口SpringBootTest(classes = SpringTestAutoConfig.class) ,则会自动从当前测试类包一层一层向上检索,直到找到@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
  • 如果制定的classes为普通Configer配置类,则会以此配置初始化Spring 上下文,而不会加载其他Bean到Spring 容器。可以在Junit测试单元中使用这些类。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {YourApplication.class})
public class BeanInjectTest {
    
    // ...
    
}

Spring MVC 测试

如果要对Spring MVC 测试,可以使用@WebMvcTest 注解,可以在不需要完整启动HTTP服务器就可以快速测试MVC控制器。
使用@WebMvcTest注解时需要注意,只有部分bean会被加载到Spring 上下文中,分别是:

  • @Controller
  • @ControllerAdvice
  • @JsonComponent
  • Filter
  • WebMvcConfigurer
  • HandlerMethodArgumentResolver

其他常规的@Component @Bean @Service注解的Bean并不会加载到Spring测试环境。如果要使用这些类,需要使用mock打桩的方式。

import static org.mockito.BDDMockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
    
@RunWith(SpringRunner.class)
@WebMvcTest(HelloController.class)
public class HelloControllerTest {
    
    @Autowired
    private MockMvc mvc;
    
    @MockBean
    private HelloService helloService;
    
    @Test
    public void testSayHi() throws Exception {
        // 模拟 HelloService.sayHi() 调用, 返回 "=== Hi ==="
        when(helloService.sayHi()).thenReturn("=== Hi ===");
        mvc.perform(get("/hello/sayHi"))
                .andExpect(status().isOk())
                .andDo(print());
    }
    
}

Spring Boot Web测试

开启Spring Http服务对Web应用测试,可以使用注解@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)注解开启一个随机的可用端口。Spring Boot针对REST接口调用提供了TestRestTemplate模版。他可以解析链接服务器的接口地址。

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationTest {
    
    @Autowired
    private TestRestTemplate restTemplate;
    
    @Test
    public void testSayHello() {
        Map<String, Object> result = restTemplate.getForObject("/hello/sayHello", Map.class);
        System.out.println(result.get("message"));
    }
    
}

Spring Data JPA测试

对Spring Data JPA进行测试可以使用@DataJpaTest注解。@DataJpaTest使用的是内存数据库进行测试,你无需配置和启用真实的数据。在pom配置文件中添加依赖即可。

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>

@DataJpaTest注解只会扫描@Entity 的Bean 和装配 Spring Data JPA 存储库,其他常规@Service @Component @Repository 注解的Bean不会加载到Spring 上下文中。

@RunWith(SpringRunner.class)
@DataJpaTest
public class UserRepositoryInMemoryTest {
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    public void testSave() {
        User user = new User();
        user.setName("fanlychie");
        userRepository.save(user);
        System.out.println("====================================");
        System.out.println(userRepository.findAll());
        System.out.println("====================================");
    }
    
}
@Entity(name = "User")
public class User {
    
    @Id
    @GeneratedValue(generator = "uuidGenerator")
    @GenericGenerator(name = "uuidGenerator", strategy = "uuid")
    private String id;
    
    private String name;
    
    // getters and setters
    
}
public interface UserRepository extends JpaRepository<User, String> {
    
}

真实数据库测试

如果需要使用真实的数据库作为测试,需要添加注解@AutoConfigureTestDatabase(replace = Replace.NONE),通知Spring不要替换原有应用的默认数据库。

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
public class UserRepositoryMySQLTest {
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    public void testSave() {
        User user = new User();
        user.setName("fanlychie");
        userRepository.save(user);
        System.out.println("====================================");
        System.out.println(userRepository.findAll());
        System.out.println("====================================");
    }
    
}

事务控制

默认情况下,在每个jpa测试执行完成时,都会执行事务回滚。从而防止测试数据污染数据库,如果不希望回滚可以在测试方法上添加注解@Rollback(false)。也可以在方法或者类上添加@Transactional作为全局的事务控制。

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
public class UserRepositoryMySQLTest {
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    @Transactional(readOnly = true)
    public void testSelect() {
        System.out.println("====================================");
        System.out.println(userRepository.findAll());
        System.out.println("====================================");
    }
    
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容