Spring 测试:使用 PowerMock mock 静态方法


#Spring#


Spring 结合 Mockito 编写单元测试讲到可以用 Mockito 模拟类/对象的行为,让我们只关心和测试自己的业务逻辑。

但是 Mockito 不支持给静态方法打桩,PowerMock 可以弥补这个不足。

关于 Mockito 的具体使用方法可以参考 Mocktio 入门

我们用一个示例说明 Spring 中如何使用 PowerMock mock静态方法。

示例

项目结构

.
├── build.gradle
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── demo
    │   │       ├── AppConfig.java
    │   │       ├── BusinessService.java
    │   │       ├── HttpService.java
    │   │       └── Main.java
    │   └── resources
    └── test
        └── java
            └── demo
                └── BusinessServiceTest.java

build.gradle

build.gradle 中声明的依赖如下:

dependencies {

    compile group: 'org.springframework', name: 'spring-context', version: '5.0.6.RELEASE'

    testCompile group: 'org.springframework', name: 'spring-test', version: '5.0.6.RELEASE'
    testCompile group: 'junit', name: 'junit', version: '4.12'
    testCompile group: 'org.mockito', name: 'mockito-core', version: '2.25.1'

    // PowerMock 相关依赖
    testCompile group: 'org.powermock', name: 'powermock-core', version: '2.0.0'
    testCompile group: 'org.powermock', name: 'powermock-module-junit4', version: '2.0.0'
    testCompile group: 'org.powermock', name: 'powermock-api-mockito2', version: '2.0.0'

}

src/main/java/demo 中是一个简单的 Spring 项目,各个类的内容如下:

HttpService.java

package demo;

import java.util.Random;

public class HttpService {

    public static int queryStatus() {           // 注意,这个特定声明为了静态方法
        // 发起网络请求,得到响应值,然后返回
        // 这里用随机数模拟
        return new Random().nextInt(2);
    }

}

BusinessService.java

package demo;

import org.springframework.stereotype.Service;

@Service
public class BusinessService {

    public String hello() {
        int status = HttpService.queryStatus();
        if (status == 0) {
            return "你好";
        }
        else {
            return "Hello";
        }
    }

}

AppConfig.java

package demo;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class AppConfig {

}

Main.java

package demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        BusinessService businessService = applicationContext.getBean(BusinessService.class);
        System.out.println(businessService.hello()); // 输出"你好",或者"Hello"

    }
}

src/test/java/demo 编写了两个关于 BusinessService 的测试类。

测试示例

BusinessServiceTest 测试类内容如下:

package demo;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(PowerMockRunner.class)                             // 必选
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)     // 必选
@PrepareForTest(HttpService.class)                          // 必选
@ContextConfiguration(classes=AppConfig.class)
public class BusinessServiceTest {

    @Autowired
    private BusinessService businessService;

    @Test
    public void testHello() {

        PowerMockito.mockStatic(HttpService.class);         // 必选

        // 打桩
        Mockito.when(HttpService.queryStatus()).thenReturn(0);

        // 测试
        Assert.assertEquals("你好", businessService.hello());  // 永远都返回 "你好"
    }

}

@PrepareForTest 中声明要mock的静态方法所在的类,可以声明多个,比如:

@PrepareForTest({HttpService.class, HttpService2.class})

( 本文完 )