鍍金池/ 教程/ Java/ 利用Mockito模擬DB
通過(guò)JMX監(jiān)控Spring Boot應(yīng)用
Spring Boot:定制PropertyEditors
配置是否初始化Bean的方法
Spring Boot的自動(dòng)配置、Command-line Runner
Spring Boot:定制URL匹配規(guī)則
Spring Boot的自動(dòng)配置、Command-line Runner
利用Mockito模擬DB
Spring Boot應(yīng)用的打包和部署
了解Spring Boot的自動(dòng)配置
Spring Boot應(yīng)用的健康監(jiān)控
了解Spring Boot的自動(dòng)配置
初始化數(shù)據(jù)庫(kù)和導(dǎo)入數(shù)據(jù)
Spring Boot應(yīng)用的健康監(jiān)控
Docker with Spring Boot
RESTful by Spring Boot with MySQL
Spring Boot:定制攔截器
Spring Boot:定制static path mappings
Spring Boot with Mysql
Spring Boot:定制自己的starter
在測(cè)試中使用內(nèi)存數(shù)據(jù)庫(kù)
Restful: Spring Boot with Mongodb
Spring Boot with Redis
Spring Boot:定制HTTP消息轉(zhuǎn)換器
Spring Boot: Data Rest Service
Spring Boot:定制type Formatters
在Spring Boot項(xiàng)目中使用Spock框架
選擇Spring Boot項(xiàng)目的內(nèi)嵌容器
通過(guò)EmbeddedServletContainerCustomizer接口調(diào)優(yōu)Tomcat
Spring Boot應(yīng)用的打包和部署
Spring Boot Admin的使用
讓你的Spring Boot工程支持HTTP和HTTPS
Spring Boot:定制servlet filters
Spring Boot:定制URL匹配規(guī)則
Spring Boot應(yīng)用的測(cè)試——Mockito
Spring Boot應(yīng)用的測(cè)試——Mockito
Spring Boot:定制servlet filters
通過(guò)@Enable*注解觸發(fā)Spring Boot配置

利用Mockito模擬DB

mockito.jpg 前兩篇文章的主要內(nèi)容是:為了給執(zhí)行測(cè)試,如何建立數(shù)據(jù)庫(kù)表和導(dǎo)入初始數(shù)據(jù)。這里我們將學(xué)習(xí)如何利用Mockito框架和一些注解模擬(mock)Repository實(shí)例,從而使得測(cè)試用例不依賴外部的數(shù)據(jù)庫(kù)服務(wù)。

我們需要?jiǎng)?chuàng)建一個(gè)Spring Boot配置類,在該類中定義用于測(cè)試的Spring Bean;我們通過(guò)注解指示Spring Boot何時(shí)加載測(cè)試配置類以及何時(shí)執(zhí)行該類中的代碼。在改配置類中,我們將使用Mockito框架創(chuàng)建一些帶預(yù)定義方法的mock對(duì)象,Spring Boot在執(zhí)行測(cè)試用例之前會(huì)將這些對(duì)象織入。

How Do

  • 首先創(chuàng)建一個(gè)注解用于標(biāo)識(shí)僅用于測(cè)試的配置類,可以按照如下方法修改BookPubApplication類??梢钥闯?,關(guān)鍵語(yǔ)句@ComponentScan(excludeFilters = @ComponentScan.Filter(UsedForTesting.class))表示:程序正式運(yùn)行時(shí)不掃描@UsedForTesting修飾的類。
@Configuration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = @ComponentScan.Filter(UsedForTesting.class))
@EnableScheduling
public class BookPubApplication {
    public static void main(String[] args) {
        SpringApplication.run(BookPubApplication.class, args);
    }
    @Bean
    public StartupRunner schedulerRunner() {
        return new StartupRunner();
    }
}

@interface UsedForTesting {}
  • src/test/java/com/test/bookpub目錄下創(chuàng)建TestMockBeansConfig文件,內(nèi)容是:
package com.test.bookpub;

import com.test.bookpub.repository.PublisherRepository;
import org.mockito.Mockito;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
@UsedForTesting
public class TestMockBeansConfig {
    @Bean
    @Primary
    public PublisherRepository createMockPublisherRepository() {
        return Mockito.mock(PublisherRepository.class);
    }
}
  • 新建一個(gè)測(cè)試類——PublisherRepositoryTests,主要是因?yàn)锽ookPubApplicationTest中的內(nèi)容太多太亂了(在實(shí)際項(xiàng)目中我們會(huì)嚴(yán)格限制每個(gè)測(cè)試類中的內(nèi)容)。
package com.test.bookpub;
import com.test.bookpub.repository.PublisherRepository;
import org.junit.After;import org.junit.Before;import org.junit.Test;
import org.junit.runner.RunWith;import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertEquals;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {
        BookPubApplication.class,
        TestMockBeansConfig.class
})
@IntegrationTest
public class PublisherRepositoryTests {
    @Autowired
    private PublisherRepository repository;

    @Before
    public void setupPublisherRepositoryMock() {
        Mockito.when(repository.count())
                .thenReturn(1L);
    }
    @Test
    public void publishersExist() {
        assertEquals(1, repository.count());
    }

    @After
    public void resetPublisherRepositoryMock() {
        Mockito.reset(repository);
    }
}

分析

OK,分析下剛剛發(fā)生了什么。首先,我們從對(duì)BookPubApplication.java的修改開始:

  • @SpringBootApplication被三個(gè)注解替換:@Configuration, @EnableAutoConfiguration@ComponentScan(excludeFilters = @ComponentScan.Filter(UsedForTesting.class)),這么做的原因是可以給@ComponentScan注解增加excludeFilters屬性,通過(guò)這個(gè)屬性,我們提示Spring Boot在正式運(yùn)行時(shí)忽略被@UsedForTesting修飾的類。
  • @UsedForTesting注解定義在BookPubApplication.java文件中,用于修飾TestMockBeansConfig類。

接下來(lái)看看在TestMockBeansConfig中的操作,

  • @Configuration注解說(shuō)明這是一個(gè)配置類,該類含有應(yīng)用程序上下文,如果被其他配置文件引入,則該類中定義的Spring Bean應(yīng)該加入到已經(jīng)創(chuàng)建的應(yīng)用上下文。
  • 修飾createMOckPublisherRepository方法的注解@Primary表示:如果在織入的時(shí)候發(fā)現(xiàn)有多個(gè)PublisherRepository的Spring Bean,則讓Spring Boot優(yōu)先使用該方法返回的Spring Bean。在應(yīng)用程序啟動(dòng)時(shí),Spring Boot根據(jù)@RepositoryRestResource注解,已經(jīng)生成一個(gè)PublisherRepository的實(shí)例,但是這里我們希望應(yīng)用程序不使用這個(gè)真實(shí)的實(shí)例,而使用Mockito框架模擬出的PublisherRepository實(shí)例。

最后看下我們的測(cè)試用例,主要關(guān)注setupPublisherRepositoryMock方法和resetPublisherRepositoryMock方法:

  • setupPublisherRepositoryMock方法被@Before注解修飾,表示在測(cè)試用例運(yùn)行之前被調(diào)用,在這個(gè)方法中我們配置了mock對(duì)象的行為:如果收到repository.count()調(diào)用,則返回1。Mockito框架提供了很多DSL形式的語(yǔ)句,可以用于定義這些容易理解的規(guī)則。

  • resetPublisherRepositoryMock方法被@After注解修飾,在測(cè)試用例執(zhí)行過(guò)后調(diào)用,用于清楚之前對(duì)repository的設(shè)置。