1. 程式人生 > >PowerMock+Mockito+MockMvc實現Controller,Service,Mapper全覆蓋測試

PowerMock+Mockito+MockMvc實現Controller,Service,Mapper全覆蓋測試

目錄

  1. 使用H2資料庫測試
  2. 使用PowerMock和Mockito進行Service層測試

    • 簡單方法如何測試
    • 複雜方法如何測試
  3. 使用MockMvc進行Controller層單元測試

    • 簡單方法如何測試
    • 需要登陸或者許可權的方法如何測試
  4. 使用SpringTest進行Dao層測試

1.使用H2資料庫測試

配置檔案: spring-config-h2db.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
>
<jdbc:embedded-database id="dataSource" type="H2" database-name="cardapy"> <jdbc:script location="classpath:h2_type.sql"/> <jdbc:script location="classpath:create-db-res.sql"/> <jdbc:script location="classpath:insert-data.sql"/> </jdbc:embedded-database
>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:conf/mybatis-config.xml"/> <property name="typeAliasesPackage" value="com.cardpay, com.cardpay.common.mapper, com.cardpay.mgt.usermanager.model"/> <!--<property name="mapperLocations" value="**/model/*.xml"/>--> <!--返回Map的攔截器--> <property name="plugins"> <array> <bean class="com.cardpay.common.intercepts.SqlTimeInterceptor"/> <bean class="com.cardpay.common.intercepts.MapInterceptor"/> <bean class="com.github.pagehelper.PageHelper"> <property name="properties"> <value> rowBoundsWithCount = true reasonable = true </value> </property> </bean> </array> </property> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 這裡如果全部掃描的話, 會有問題--> <property name="basePackage" value="com.cardpay"/> <property name="markerInterface" value="com.cardpay.common.base.dao.IBaseDao"/> </bean> <!-- (事務管理)transaction manager, use JtaTransactionManager for global tx --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> </beans>

資料庫型別:這裡寫圖片描述

SET MODE MySQL;

資料庫指令碼:這裡寫圖片描述
需要注意的是SQL指令碼不支援註釋,需要刪除如下後才能執行

-- ----------------------------
-- Table structure for b_repayment_plan
-- ----------------------------
DROP TABLE IF EXISTS b_repayment_plan;
CREATE TABLE b_repayment_plan (
  id int(11) NOT NULL AUTO_INCREMENT,
  application_id int(11) DEFAULT NULL ,
  status tinyint(2) DEFAULT NULL ,
  repay_date timestamp NULL DEFAULT NULL ,
  loan_term varchar(32) DEFAULT NULL ,
  monthly_payment varchar(32) DEFAULT NULL ,
  real_repay_date timestamp NULL DEFAULT NULL ,
  PRIMARY KEY (id),
  KEY repayment_applicatin_fk_idx73 (application_id) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8;

插入的資料:這裡寫圖片描述
配置檔案中已經指明database-name所有插入資料時SQL中不需要指定資料庫

INSERT INTO `t_application_var` VALUES ('8', '1', '689', '1', null);
INSERT INTO `t_application_var` VALUES ('561896', '1945', '580', '100', '2');

2.使用PowerMock和Mockito進行Service層測試

1. 簡單方法

在任何需要用到 PowerMock 的類開始之前,首先我們要做如下宣告:
@RunWith(PowerMockRunner.class)
然後,還需要用註釋的形式將需要測試的靜態方法提供給 PowerMock:
@PrepareForTest( { YourClassWithEgStaticMethod.class })

@mock為一個interface提供一個虛擬的實現。
@InjectMocks將本test類中的mock(或@mock)注入到被標註的物件中去,也就是說被標註的物件中需要使用標註了mock(或@mock)的物件。
在測試中,可以使用 When().thenReturn()語句來指定被引用的方法返回任意需要的值,達到覆蓋測試的效果。
assertEquals為Junit的斷言方法,驗證返回值是否為預期值
verify驗證在測試中是否呼叫了指定的方法並且是預期的值

@RunWith(PowerMockRunner.class)
@PrepareForTest({ExpertApplicationServiceImpl.class})
public class ExpertApplicationServiceImplTest {

    @Mock
    private ExpertApplicationMapper expertApplicationMapper;

    @Mock
    private ApplicationMapper applicationMapper;

    @InjectMocks
    private ExpertApplicationServiceImpl expertApplicationService;

    @Test
    public void update() throws Exception {
        ExpertApplication expertApplication = new ExpertApplication();
        List<ExpertApplication> expertApplicationList = new ArrayList<>();
        when(expertApplicationMapper.update(expertApplication)).thenReturn(1);
        int updateResult = expertApplicationService.update(expertApplication);
        assertEquals(1,updateResult);
        verify(expertApplicationMapper).update(expertApplication);
    }

原方法:

    @Override
    public int update(ExpertApplication obj) {
        return expertApplicationDao.update(obj);
    }

2. 複雜方法

這裡介紹一下稍微複雜一點的方法如何測試
這個方法主要問題是在原方法中使用添加了new出來的物件並呼叫了add方法
如果不做任何處理的話在verify方法add時會丟擲異常,因為測試中new出的物件和verify驗證的物件並不是同一個物件。解決辦法是使用:
whenNew(ExpertApplication.class).withAnyArguments().thenReturn(expertApplication);
這個方法可以在測試程式碼中new的物件也替換為做自己指定的物件!
測試方法:

    @Test
    public void division() throws Exception {
        Integer applicationId = 1234;
        String ids = "1,2,3,4,5";
        String finalId = "6";
        Application application = new Application();
        application.setId(applicationId);
        ExpertApplication expertApplication = new ExpertApplication();

        when(applicationMapper.get(applicationId)).thenReturn(application);
        whenNew(ExpertApplication.class).withAnyArguments().thenReturn(expertApplication);

        expertApplicationService.division(applicationId,ids,finalId);

        verify(expertApplicationMapper).delete(applicationId);
        verify(expertApplicationMapper,times(6)).add(expertApplication);
    }

原方法:

    @Override
    public int division(Integer applicationId, String expertIds, String finalExpertId) {
        expertApplicationDao.delete(applicationId);
        String[] expertIdArray = expertIds.split(",");
        Date date = new Date();
        //--------------給普通專家進行分案----------
        for (String expertId : expertIdArray) {
            ExpertApplication expertApplication = new ExpertApplication();
            expertApplication.setExpertId(Integer.parseInt(expertId));
            expertApplication.setApplicationId(applicationId);
            expertApplication.setExpertEvaluationResult(EXPERT_REVIEWING.getStatusKey());
            expertApplication.setIfBoss(EXPERT);
            expertApplication.setCreatedAt(date);//建立時間
            expertApplication.setModifiedAt(date);//更新修改時間
            expertApplicationDao.add(expertApplication);
        }
        //--------------新增終審專家資料------------------------
        ExpertApplication expertApplication = new ExpertApplication();
        expertApplication.setExpertId(Integer.parseInt(finalExpertId));
        expertApplication.setApplicationId(applicationId);
        expertApplication.setExpertEvaluationResult(EXPERT_REVIEWING.getStatusKey());
        expertApplication.setIfBoss(FINAL_EXPERT);
        expertApplication.setCreatedAt(date);//建立時間
        expertApplication.setModifiedAt(date);//更新修改時間
        expertApplicationDao.add(expertApplication);
        //-------------------更新進件狀態---------------------------------------
        Application application = applicationDao.get(applicationId);
        application.setStatus(EXPERT_REVIEWING.getStatusKey());// 專家評審中
        return applicationDao.update(application);
    }

3.使用MockMvc進行Controller層單元測試

首先需要繼承基類

@Rollback
@Transactional
@WebAppConfiguration
@ContextConfiguration(name = "child", locations = {"classpath:pccims-servlet-test.xml"})
public abstract class TestEnv extends BaseTest {

    protected MockMvc mockMvc;

    protected Subject subject;

    protected static User baseUser;

    static {
        baseUser = User.UserBuilder.get().withId(1).withName("manager").withEmail("[email protected]")
                .withPassword("7d9ca79d6ead7127c3a17fd00c1e6090").build();
    }

    @Autowired
    private WebApplicationContext webApplicationContext;

    @Before
    public void setUp() throws Exception {
        SecurityManager securityManger = PowerMockito.mock(SecurityManager.class, Mockito.RETURNS_DEEP_STUBS);
        ThreadContext.bind(securityManger);
        subject = new Subject.Builder(getSecurityManager()).buildSubject();
        setSubject(subject);
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    protected void setUser(User user, String role) {
        PowerMockito.when(subject.getPrincipal()).thenReturn(user);
        if (StringUtils.isNotEmpty(role)) {
            PowerMockito.when(subject.hasRole(role)).thenReturn(true);
        } else {
            role = user.getName();
            switch (role) {
                case Constant.ROLE_ADMIN:
                    PowerMockito.when(subject.hasRole(Constant.ROLE_ADMIN)).thenReturn(true);
                    break;
                case Constant.ROLE_MANAGER:
                    PowerMockito.when(subject.hasRole(Constant.ROLE_MANAGER)).thenReturn(true);
                    break;
                case Constant.ROLE_LEADER:
                    PowerMockito.when(subject.hasRole(Constant.ROLE_LEADER)).thenReturn(true);
                    break;
                case Constant.ROLE_EXPERT:
                    PowerMockito.when(subject.hasRole(Constant.ROLE_EXPERT)).thenReturn(true);
                    break;
                default:
                    PowerMockito.when(subject.hasRole(role)).thenReturn(false);
            }
        }
    }

    protected void setUser(User user) {
        setUser(user, null);
    }
}

1.簡單方法

目前介面都是返回json,所以主要測試的就是返回的json是否正確
如何使用JsonPath請參考:
mockMvc.perform執行一個請求;
delete("/api/experts/1")構造一個請求
andExpect新增執行完成後的斷言

    @Test
    public void deleteTest() throws Exception {
        mockMvc.perform(delete("/api/experts/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

2.需要登陸或者許可權的方法

baseUser預設是客戶經理角色,如需要其他角色許可權可以自行建立User並set

    @Test
    public void add() throws Exception {
        setUser(baseUser);
        mockMvc.perform(post("/api/experts").param("name","A").param("cname","趙鐵柱"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

4.使用SpringTest進行Dao層測試

繼承基類
注意此基類和Controller層基類不同

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@PowerMockIgnore("javax.management.*")
@ContextConfiguration(locations={ "classpath:spring-core-config-test.xml","classpath:pccims-servlet-test.xml"})
//配置事務的回滾,對資料庫的增刪改都會回滾,便於測試用例的迴圈利用
@Rollback
@Transactional
public abstract class TestEnv{}

Dao層測試比較簡單隻需要在資料庫插入需要的測試資料並且呼叫mapper方法測試SQL返回的資料是否和預期一樣即可


    @Autowired
    private ExpertMapper expertMapper;

    @Test
    public void findByDivisionalRules() throws Exception {
        Expert expert = new Expert();
        expert.setAreaDivisionalRules("陝西");
        expert.setProductType("3");
        List<Expert> expertList = expertMapper.findByDivisionalRules(expert);
        assertTrue(expertList.size() > 0);
    }

相關推薦

PowerMock+Mockito+MockMvc實現Controller,Service,Mapper覆蓋測試

目錄 使用H2資料庫測試 使用PowerMock和Mockito進行Service層測試 簡單方法如何測試 複雜方法如何測試 使用MockMvc進行Controller層單元測試 簡單方法如何測試 需要登陸或者許可權的方法如何測試 使用Spring

SpringBoot專案,使用MockMvccontroller類的JUNIT單元測試

package hello; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.t

Mybatis(攔截器實現)通用mapperORM實現(四)

到目前為止,我們通過mybatis的攔截器完成了一些基礎mapper的功能,接下來我們來看看如何能夠實現一個物件關係對映的全ORM操作。 實體與表、列的定義已經完成了,那剩下要做的就是: 1、定義如何通過物件方式編寫sql語句 2、把查詢物件轉譯成sql去查詢 3、把查詢結

Mybatis(攔截器實現)通用mapperORM實現(五)-- springboot+mybatis多資料來源設定

本篇實際上和mybatisext專案並沒有太大關係了,但在實際專案中脫離不開多個數據源,尤其是主從分離,同樣網上一些資料大同小異而且大部分並不能真正解決問題,所以單獨提出來說一下 假設我們就是要解決一個主從分離,資料來源定義在了application.properties中

springboot+mybatis+springmvc實現資料庫增加資料,除錯時service實現類中mapper物件為空

問題:service實現類裡面執行到Mapper.save(Entity)時,捕捉到空指標異常 通過除錯,發現Mapper為空 解決途徑: 在瀏覽多個回答後,在論壇裡面看到有人回答說,controller層的方法中new 了*ServiceImpl()導致,如下圖 解決方法:註釋掉該條

spring boot專案 mybatis CodeGenerator程式碼自動生成entityVO,controller,service,serviceimpl,dao,mapper程式碼

新專案需要使用spring boot,用freemarker寫程式碼生成模板,能省下很多時間。 因為專案需要entity,requestVO,responseVO,所以需要獲取資料庫資料生成封裝類。 廢話不多說,上程式碼記錄一下。 注:資料型別不全,我只寫了我能用到的

SpringAOP攔截Controller,Service實現日誌管理(自定義註解的方式)

         首先我們為什麼需要做日誌管理,在現實的上線中我們經常會遇到系統出現異常或者問題。這個時候就馬上開啟CRT或者SSH連上伺服器拿日子來分析。受網路的各種限制。於是我們就想為什麼不能直接在管理後臺檢視報錯的資訊呢。於是日誌管理就出現了。          其次

使用Spring的@Autowired 實現DAO, Service, Controller三層的注入

簡述: 結合Spring和Hibernate進行開發 使用@Autowired實現依賴注入, 實現一個學生註冊的功能,做一個技術原型 從DAO(Repository) -> Service -> Controller 目錄結構: 使用Maven做本地包管理,

SpringAOP攔截Controller,Service實現日誌管理(自定義註解的方式)(轉載)

http://langgufu.iteye.com/blog/2235556   首先我們為什麼需要做日誌管理,在現實的上線中我們經常會遇到系統出現異常或者問題。這個時候就馬上開啟CRT或者SSH連上伺服器拿日子來分析。受網路的各種限制。於是我們就想為什麼不能直接在

jquery實現復選框選,不選,反選中的問題

了解 span htm cli ems 添加 send 籃球 需要 今天試了一下用jquery選擇復選框,本來以為很簡單的東西卻有bug,於是搜索了一下找到了解決方法。 html代碼如下(這裏沒有用任何樣式,就沒有再放css了): <html> <

在CentOS7上通過RPM安裝實現LAMP+phpMyAdmin過程記錄

isp -s 管理工具 gpgcheck b2c link 5.5 art http 在CentOS7上通過RPM安裝實現LAMP+phpMyAdmin過程全記錄 時間:2017年9月20日 一、軟件環境: IP:192.168.1.71 Hostname:centos73

spring自動掃描的註解@Component @Controller @Service @Repository

改變 包括 alt init 實例 gda context nts str @Component @Controller @Service @Repository的作用 1、@controller 控制器(註入服務)2、@service 服務(註入dao)3、@reposi

js 實現復選框選 全部選

tails elements 全選 str byname false public xhtml under http://blog.csdn.net/sunwei3160/article/details/38515187 <!DOCTYPE html PUB

js實現復選框選和不選

onclick check checkbox rip () nbsp title false style <html> <head> <title></title> </head> <style>

【JS】用checked實現複選框選和不選

<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf8"> <title>無標題文件</title>

spring基於MockMvccontroller測試

@Component public class TestControllerUtil { private MockMvc mockMvc; @Autowired private WebApplicationContext webApplicationContex

js實現複選框選/不選/反選

js實現複選框全選/全不選/反選 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> &

JQuery 實現複選框選反選功能

  HTML部分 <body> <input type="checkbox" name="fu">全選(父)<br> <input type="checkbox" name="zi">子1<br> <input ty

react-native ScrollView 實現上拉滑動屏,下拉恢復原先大小

ScrollView 系列的都可以完成, 比如 FlatView 和 SectionList 都可以。 1 需求 大概就是一個 scroll 元件向上滑動的時候可以完全展示出來。完全展示之後下滑再恢復縮小時的高度。 1.1需求分析 縮小時不允許滾動

jquery實現複選框不選

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>使用jQuery完成複選框的全選和全不選</title> <script t