Mock&Spring集成


Mock&Spring集成#

常規Mock單元測試##

請參考上一篇文檔Mock

mock框架的功能性對比##

http://jmockit.github.io/MockingToolkitComparisonMatrix.html

從模擬支持特性上做了詳細的對比,比如是否支持模擬static、構造函數等等。

集成測試##

大部分Web應用項目基於Spring平台構建,集成測試主要關注點是Junit+Spring+Mock集成!
從Spring項目2.x開始就有基於Junit的測試輔助包(Spring-test)!
重點關注引入Mock框架后Spring與其集成!

溫馨提示Jmockit作為本文代碼示例的模擬框架,其它模擬選型框架的測試結果直接作為結論。

關注點##

Mock測試框架是否能夠和Spring完美集成?

  • 版本&升級(mock框架版本和Spring版本是否完全兼容,無包沖突,是否可升級,比如Spring從2.x->3.x->4.x)
  • 內容&特性(mock框架提供的各種模擬策略特性能否和Spring-test提供的各種測試支持類兼容)

基於Spring的測試點##

虛擬機運行環境版本: 1.7.0_60

1)基礎包版本:

  • Spring:2.5.6/3.x.x/4.x.x, Junit:4.x

2)Mock選型:

  • Mockito:1.9.5, PowerMock-mockito-*: 1.5.5
  • Jmock:2.6.0
  • Jmockit:1.14

3)模擬測試用例

  • 模擬Spring單例bean方法
  • 模擬靜態方法調用

備注:實際測試是分開多個測試Demo,spring+mockito

示例代碼##

UserService,StaticUserService,UserAction類請參考上一篇文檔Mock

靜態公共方法模擬###

package jmockit;

import mockit.NonStrictExpectations;
import mockit.Verifications;

import org.junit.Test;
import org.wit.service.StaticUserService;
import org.wit.service.UserAction;

public class MockForPublicStaticDemo {

    @Test
    public void demo() {
        new NonStrictExpectations(StaticUserService.class) {
            {
                StaticUserService.sayHello(anyString);
                result = "mock";
            }
        };

        // assertEquals("mock", StaticUserService.sayHello("real"));
        UserAction userAction = new UserAction();
        userAction.executeForPublicStatic1("real");

        new Verifications() {
            {
                StaticUserService.sayHello(anyString);
                times = 1;
            }
        };
    }
}

Spring Singleton Bean方法模擬###

package jmockit;

import mockit.NonStrictExpectations;
import mockit.Verifications;

import org.junit.Test;
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.wit.service.StaticUserService;
import org.wit.service.UserAction;
import org.wit.service.UserService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class MockForSpringJunitDemo {

    @Autowired
    private UserAction userAction;
    
    @Autowired
    private UserService userService;

    @Test
    public void demo() throws Exception {
        new NonStrictExpectations(StaticUserService.class) {
            {
                StaticUserService.sayHello(anyString);
                result = "mock";
            }
        };

        userAction.executeForPublicStatic1("real");

        new Verifications() {
            {
                StaticUserService.sayHello(anyString);
                times = 1;
            }
        };
        
    }
    
    /**
     * 
     * <pre>
     * 模擬bean的方法.
     * UserService sayHello模擬調用, sayHi為真實調用.
     * </pre>
     *
     * @throws Exception
     */
    @Test
    public void beanDemo() throws Exception{
        new NonStrictExpectations(userService) {
            {
                userService.sayHello(anyString);
                result = "mock";
            }
        };

        userAction.executeForPublic("hi");

        new Verifications() {
            {
                userService.sayHello(anyString);
                times = 1;
            }
        };
        
    }
}

靜態私有方法模擬###

package jmockit;

import static mockit.Deencapsulation.invoke;
import mockit.Expectations;
import mockit.Verifications;

import org.junit.Test;
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.wit.service.StaticUserService;
import org.wit.service.UserAction;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class MockForSpringJunitPrivateStaticDemo {

    @Autowired
    private UserAction userAction;

    @Test
    public void demo() throws Exception {

        new Expectations(StaticUserService.class) {
            {
                invoke(StaticUserService.class, "secreteSayHi", anyString);
                invoke(StaticUserService.class, "secreteSayHello", anyString);
                result = "mock";
            }

        };

        userAction.executeForPrivateStatic("real");

        new Verifications() {
            {
                invoke(StaticUserService.class, "secreteSayHi", anyString);
                times = 1;
                invoke(StaticUserService.class, "secreteSayHello", anyString);
                times = 1;
            }
        };
    }

}

結論##

方案:Mockito:1.9.5, PowerMock-mockito-*: 1.5.5

  • Spring-test 3.x以上版本啟用了新測試基類,並提供新的測試運行器(SpringJunit4ClassRunner.class), 它會和PowerMock提供的測試運行器(PowerMockRunner.class)產生沖突,如果選擇PowerMockRunner會導致找不到Spring配置文件的錯誤發生;
  • Spring-test 2.5.6的測試基類AbstractDependencyInjectionSpringContextTests和PowerMock兼容,但此類在Spring3.x之后的版本中過時。
  • 采用官方提供的PowerMockRule會產生虛擬機崩潰的錯誤詳細日志見下文
2015-11-27 15:58:48,015 INFO  context.TestContextManager - Could not instantiate TestExecutionListener class [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their dependencies) available.
2015-11-27 15:58:48,019 INFO  context.TestContextManager - Could not instantiate TestExecutionListener class [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their dependencies) available.
2015-11-27 15:58:48,112 INFO  xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [applicationContext.xml]
2015-11-27 15:58:48,191 INFO  support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@361fc5a2: startup date [Fri Nov 27 15:58:48 GMT+08:00 2015]; root of context hierarchy
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000000291e2ba, pid=15700, tid=14928
#
# JRE version: Java(TM) SE Runtime Environment (7.0_60-b13) (build 1.7.0_60-ea-b13)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (24.60-b09 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# j  java.lang.reflect.ReflectAccess.copyField(Ljava/lang/reflect/Field;)Ljava/lang/reflect/Field;+1
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# D:\workspace\workspace-demos\mockito\hs_err_pid15700.log
Compiled method (c2)     861   16             java.lang.Object::<init> (1 bytes)
 total in heap  [0x0000000002969310,0x0000000002969540] = 560
 relocation     [0x0000000002969430,0x0000000002969440] = 16
 main code      [0x0000000002969440,0x00000000029694c0] = 128
 stub code      [0x00000000029694c0,0x00000000029694d8] = 24
 oops           [0x00000000029694d8,0x00000000029694e0] = 8
 scopes data    [0x00000000029694e0,0x00000000029694f0] = 16
 scopes pcs     [0x00000000029694f0,0x0000000002969520] = 48
 dependencies   [0x0000000002969520,0x0000000002969528] = 8
 handler table  [0x0000000002969528,0x0000000002969540] = 24
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.sun.com/bugreport/crash.jsp
#

方案: Jmock:2.6.0,Jmockit:1.14

  • Jmock&Jmockit在測試運行器上選擇Spring的測試運行器,和Spring各個版本兼容良好。
  • 模擬Spring 單例Bean的方法和靜態方法都能正常模擬,請關注靜態私有方法和公共方法的差異

QA##


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM