您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 订阅
  捐助
微服务架构下单元测试落地实践(下)
 
作者:高晓鹏
   次浏览      
 2021-8-20
 
编辑推荐:
本文章主要针对项目中涉及到的单元测试的场景进行代码演示,首先从概念,核心API和注解,如何使用对PowerMock框架进行介绍。
本文来自于葫芦APP知识中心,由火龙果软件Anna编辑、推荐。

上篇介绍了单元测试的相关概念以及微服务架构下单元测试的mock框架的选型,该篇主要针对项目中涉及到的单元测试的场景进行代码演示,首先对使用的PowerMock框架进行简单的介绍。

微服务架构下单元测试落地实践(上)(点击查看)

一、PowerMock简介

1.概念

Mockito是Java流行的一种Mock框架,Mock技术的使用可以让我们单元测试隔离外部依赖,免除了复杂外部依赖的构建工作,使得单元测试更加容易进行。其工作原理是创建依赖对象的一个Proxy对象,调用时,对依赖对象的调用都是经过Proxy对象,Proxy对象拦截了所有的请求,只是按照预设值返回。但是Mockito在单元测试时,有一些场景无法解决,它不提供对静态方法、构造方法、私有方法以及 Final 方法的模拟支持,这里就需要PowerMock了,因此可以把PowerMock理解为Mockito的增强,它解决了Mockito一些不能解决的场景。

2.核心API和注解

A a = PowerMockito.mock (A.Class)
PowerMockito.when(a.method()).thenReturn (obj)

上面两行代码描述了使用PowerMock的基本流程,第一行代码表示创建了A的一个虚拟对象a;第二行指定了这个虚拟对象的一个行为,在调用a的method()方法时,返回了一个obj对象。这两行代码完成了外部依赖的模拟过程。

@Mock:
创建一个虚拟对象
@InjectMocks:
创建一个实例,其余用@Mock(或@Spy)
注解创建的对象将被注入到用该实例中

这两个核心注解用来在单元测试类中构建类的依赖关系,不需要依赖spring环境,使得单元测试更轻量级,这在后面的示例代码中会有体现。

3. 使用

PowerMock的使用很简单,直接在pom中引入相应的依赖即可,需要注意的一点,由于PowerMock依赖Mockito,因此两者依赖需要同时引入,且两者版本有严格的对应关系,应当按照官方指定的版本依赖关系才能保证正常使用。这里是基于mockito 2.8.9和powermock 1.7.4。

<properties>
<mockito.version>2.8.9</mockito.version>
<powermock.version>1.7.4</powermock.version>
<properties/>

<dependencies>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependencies/>

二、PowerMock单元测试场景

以下内容为我们项目中单元测试可能遇到的场景,PowerMock都可以完成测试。

1. dao接口模拟

@Service
public class ConsumerService {

@Autowired
private ConsumerMapper consumerMapper;

public String helloDB(String name){
System.out.println("调用数据库前的业务逻辑……");
String result = consumerMapper.callDB(name);
System.out.println("调用数据库后的业务逻辑……");
return result;
}
}

@Mapper
public interface ConsumerMapper {
String callDB(String name);
}

单元测试类如下,该测试类及后续的示例中只展示对应情形下所需要注入的类和依赖:

// @RunWith表明当前单元测试类使用PowerMock框架
@RunWith(PowerMockRunner.class)
public class PowerMockDaoTest {

// 创建一个consumerService
@InjectMocks
ConsumerService consumerService;

// 创建一个虚拟的consumerMapper,
并且在下面setup方法后会自动注入到consumerService中
@Mock
ConsumerMapper consumerMapper;

@Before
public void setup(){
// 根据类的不同注解,创建不同的mock对象,
同时完成装配工作,即整个单元测试的初始化
MockitoAnnotations.initMocks(this);
}

@Test
public void test() {
String name = "hulu";
// 指定虚拟的的consumerMapper的行为,
调用callDB("hulu")时,返回hello hulu, this is DB
PowerMockito.when (consumerMapper.callDB(name)).thenReturn ("hello hulu, this is DB");
// 调用service方法
String result = consumerService.helloDB (name);
//最后这里加上断言,进行判断执行结果是否达到我们的预期
Assert.assertEquals ("hello hulu, this is DB",result);
}
}

以上是经常需要mock的一种情况,service中调用了dao接口,使用mock后,可以在调用dao接口方法时,让该方法按照我们指定的入参返回指定的返回值,这个过程不需要真实连接数据库,实现屏蔽数据库的第三方依赖。

上图展示了真实调用过程中执行到callDB("hulu")时,consumerMapper的真实状态,可以看到已经变成了Mock框架生成的代理对象,代理对象在执行相应方法时,按照指定的预期值进行了返回。

2. feign远程调用模拟

下面是微服务中,使用feign调用远程服务的示例代码:

@Service
public class ConsumerService {

@Autowired
HelloRemote helloRemote;
// 通过helloRemote远程调用的其他服务
public String helloRemote (@PathVariable("name") String name) {
System.out.println ("其他业务逻辑");
String hello = helloRemote.hello(name);
System.out.println ("其他业务逻辑");
return hello;
}
}

@FeignClient
(name="service-producer",fallback=HelloRemoteFallback.class)
public interface HelloRemote {
@RequestMapping (value="/hello")
String hello (@RequestParam(value = "name") String name);
}

单元测试示例如下,和上述的屏蔽数据库依赖类似:

@RunWith (PowerMockRunner.class)
public class PowerMockFeignTest {

@InjectMocks
ConsumerService consumerService;

@Mock
HelloRemote helloRemote;

@Before
public void setup(){
MockitoAnnotations.initMocks (this);
}

@Test
public void test() {
String name = "hulu";
String mockResult = "hello hulu, this is fallback";
// 指定调用远端服务接口时返回预期值
PowerMockito.when (helloRemote.hello(name)).thenReturn (mockResult);
// 调用service方法
String result = consumerService.helloRemote (name);
//判断执行结果是否达到我们的预期
Assert.assertEquals (mockResult,result);
}

}

同样上述的远程服务调用并不需要远程服务真实可用,使用框架指定了调用方法的返回值,方法就可以按照预期值返回,保证这个方法不依赖远程服务即可完成单元测试。

3. redis调用模拟

分布式系统中redis是最常用的中央缓存,以下是对redis的mock过程。示例代码如下:

@Service
public class ConsumerService {
// 这里直接使用redisTemplate操作redis,
实际项目中可能会再封装一层
@Autowired
private RedisTemplate<String, String> redisTemplate;

public String helloRedis(String key){
System.out.println("调用redis前的业务逻辑……");
String result = redisTemplate.opsForValue().get(key);
System.out.println ("调用redis后的业务逻辑……");
return result;
}
}

@RunWith(PowerMockRunner.class)
public class RedisTest {

@InjectMocks
ConsumerService consumerService;

@Mock
private RedisTemplate<String, String> redisTemplate;

@Before
public void setup(){
// 这句话执行以后,redisTemplate自动注入到consumerService中
MockitoAnnotations.initMocks(this);
}

@Test
public void test() {
String key = "test";
// 对redisTemplate.opsForValue().get("key")
进行mock 这也是一个级联方法的mock
ValueOperations valueOperations = PowerMockito.mock (ValueOperations.class);
PowerMockito.when (redisTemplate.opsForValue()).thenReturn (valueOperations);
PowerMockito.when (valueOperations.get ("test")).thenReturn ("this is mock redis");
// 调用service方法
String result = consumerService.helloRedis (key);
// 判断执行结果是否达到我们的预期
Assert.assertEquals ("this is mock redis",result);
}
}

上述示例的特殊之处在于调用的方法是一个级联方法redisTemplate.opsForValue().get(key); 对这类调用的mock需要分步进行,即先指定redisTemplate.opsForValue()的返回值,然后再指定其返回值get(key)的返回值,完成一个级联方法的模拟。

4. kafka调用的模拟

项目中使用了kafka,实现了对kafka生产者和消费者的模拟。示例代码如下:

@Service
public class ConsumerService {

@Autowired
private KafkaTemplate kafkaTemplate;

/**
* 生产者
*/
public String sendMessage (String topic,String value){
System.out.println ("向kafka发送消息前执行的逻辑");
ListenableFuture send = kafkaTemplate.send (topic, value);
System.out.println ("向kafka发送消息后执行的逻辑");
return "success";
}

/**
* 消费者
*/
@KafkaListener(topics = {"${kafka.consumer.topic}"})
public void consumeMessage (ConsumerRecord<?, ?> record) throws IOException {
String value = (String) record.value();
System.out.println ("当前获取的消息内容为:"+value);
System.out.println ("使用消息执行了一些逻辑");
}
}

@RunWith (PowerMockRunner.class)
@PrepareForTest({ConsumerRecord.class})
public class KafkaTest {

@InjectMocks
ConsumerService consumerService;

@Mock
private KafkaTemplate kafkaTemplate;

@Before
public void setup(){
MockitoAnnotations.initMocks(this);
}
/**
* 生产者测试
*/
@Test
public void testSend() {
String topic = "result";
String value = "这是一条测试的消息";
// 首先指定mock send方法的返回值
ListenableFuture mockFuture = PowerMockito.mock (ListenableFuture.class);
// 指定send方法调用时候的动作
PowerMockito.when(kafkaTemplate.send (topic,value)).thenReturn(mockFuture);
String result = consumerService.sendMessage (topic,value);
Assert.assertEquals ("success",result);
}
/**
* 消费者测试
*/
@Test
public void testConsume() throws IOException {
ConsumerRecord mockRecord = PowerMockito.mock (ConsumerRecord.class);
PowerMockito.when ((String)mockRecord.value()).thenReturn ("value");
consumerService.consumeMessage (mockRecord);
}
}

@PrepareForTest({ConsumerRecord.class}),这里简要说明一下它的作用:当mock一些特殊方法时,如static,final,private,系统类static方法时,这些方法所在的类需要在@PrepareForTest里指明,否则会报不能mock该类的异常:

org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: class org.apache.kafka.clients.consumer.ConsumerRecord.
Mockito can only mock non-private & non-final classes.
If you're not sure why you're getting this error, please report to the mailing list.

Mockito无法模拟一个ConsumerRecord,因为ConsumerRecord是一个final类,必须在注解中指明。这也是PowerMock相对于Mockito的过人之处,解决了Mockito无法mock特殊方法的痛点;另外当使用PowerMockito.whenNew方法时,必须加注解@PrepareForTest,注解@PrepareForTest里写的类是需要mock的new对象代码所在的类,这一点在后面构造方法的模拟中会有体现。

6. session的模拟

项目使用了SpringSession+Redis的分布式session解决方案,因此经常需要从session中获取当前用户id,由于request和session对象的复杂性,在这类情形下编写单元测试非常困难,因为无法手动创建一个符合预期的request或者session对象,使用mock框架可以轻松解决。

@Service
public class ConsumerService {

public String getUserName (HttpServletRequest request){
// 从session中获取用户id
String userId = (String)request.getSession().getAttribute ("userId");
System.out.println("这里还执行了一些其他逻辑");
// 假设根据用户id查询了数据库
String userName = consumerMapper.callDB (userId);
// 返回用户姓名
return userName;
}
}

上述代码描述了一个常见场景,从session中获取用户id,并根据用户id查询数据库获取用户信息的示例。单元测试代码如下:

@RunWith (PowerMockRunner.class)
public class SessionTest {

@InjectMocks
ConsumerService consumerService;

@Mock
ConsumerMapper consumerMapper;

@Before
public void setup() {
MockitoAnnotations.initMocks (this);
}

@Test
public void test() {
// mock HttpServletRequest
HttpServletRequest mockRequest = PowerMockito.mock (HttpServletRequest.class);
// mock HttpSession
HttpSession mockSession = PowerMockito.mock (HttpSession.class);
// 指定了request调用getSession时返回mockSession
PowerMockito.when (mockRequest.getSession()).thenReturn(mockSession);
// 指定session获取userId时返回123
PowerMockito.when (mockSession.getAttribute("userId")).thenReturn("123");
// 指定了用123去查询数据库返回的用户zhangsan
PowerMockito.when (consumerMapper.callDB("123")).thenReturn("zhangsan");
// 调用service方法
String result = consumerService.getUserName (mockRequest);
//最后这里加上断言,进行判断执行结果是否达到我们的预期
Assert.assertEquals ("zhangsan", result);
}
}

7. controller层单元测试模拟

通常按照代码规范,controller层是不允许有业务逻辑的存在,因此也谈不上需要进行单元测试了。但是若由于某些原因存在业务逻辑需要进行单元测试,使用框架也是可以实现的。示例代码如下:

@RestController
public class ConsumerController{

@Autowired
private ConsumerService consumerService;

@RequestMapping (value="/helloLocal/{name}")
public String helloLocal(@PathVariable ("name") String name){
System.out.println ("一些不该存在的业务逻辑");
String result = consumerService.helloLocal(name);
System.out.println ("一些不该存在的业务逻辑");
return result;
}
}


@RunWith(PowerMockRunner.class)
public class ControllerTest {

@Mock
private ConsumerService service;

@InjectMocks
private ConsumerController consumerController;

@Test
public void testHelloLocal(){
String name = "hulu";
// 指定mockservice方法的行为
PowerMockito.when (service.helloLocal(name)).thenReturn ("hello hulu, this is mock local");
String result = consumerController.helloLocal(name);
Assert.assertEquals ("hello hulu, this is mock local",result);
}
}

观察以上代码发现方法和前面的测试实现思路一样,只是原来待注入的类是service,现在变成了controller,模拟一个service对象,指定其行为,完成controller层的单元测试,验证除该行外其他的业务逻辑。

8. 构造方法的模拟

构造方法的mock主要是针对代码中使用对象的构造方法创建对象。下面是自定义的一个实体,包含了无参构造和有参构造。

public class NewClass {

private String message;
// 无参构造
public NewClass(){}
// 有参构造
public NewClass(String message) {
this.message = message;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}
}

下面是一个类中使用了相应的构造方法


public class TargetClass {

public String getMessage(){
// 使用无参构造创建了一个对象
NewClass object= new NewClass();
return object.getMessage();
}

public String getInsertMessage(){
// 使用有参构造构建了一个对象
NewClass object= new NewClass ("this is another message");
return object.getMessage();
}
}

单元测试示例代码如下


@RunWith(PowerMockRunner.class)
// 需要在@PrepareForTest中指明这两者
@PrepareForTest({NewClass.class,TargetClass.class})
public class NewObjectTest {

/**
* 这种new对象的mock也可以理解成是mock无参构造的方法
*/
@Test
public void testConstructionMethodWithNoArg() throws Exception {
// 要mock的new的类
NewClass newClass = PowerMockito.mock (NewClass.class);
// 这里是当使用无参构造时候返回我们mock的实体
PowerMockito.whenNew (NewClass.class).withNoArguments().thenReturn (newClass);
// 指定当调用mock的这个实体的方法时候返回指定的值
String mockResult = "this is mock message";
PowerMockito.when (newClass.getMessage()).thenReturn(mockResult);
// 创建我们要测试的目标类,并执行我们的方法
TargetClass targetClass = new TargetClass();
String message = targetClass.getMessage();
// 校验返回值
Assert.assertEquals (mockResult,message);
}

/**
* mock 有参构造
*/
@Test
public void testConstructionMethodWithArg() throws Exception {
// 先mock一个这个类的对象
NewClass newClass = PowerMockito.mock(NewClass.class);
// 这里是当使用有参构造时候返回我们指定的mock对象
String message = "this is another message";
PowerMockito.whenNew(NewClass.class).withArguments (message).thenReturn(newClass);
// 指定当调用mock对象的方法时候的返回值
String mockMessage = "this is another mock message";
PowerMockito.when (newClass.getMessage()).thenReturn (mockMessage);
// 这里创建包含mock对象的类,这里进行方法测试
TargetClass targetClass = new TargetClass();
String returnMessage = targetClass.getInsertMessage();
// 校验返回值
Assert.assertEquals (mockMessage,returnMessage);
}
}

9. final方法的模拟

一个持有final方法的类:

public class FinalMethodClass {

public final String finalMethod(){
return "this is finalMethod";
}
}

一个调用final方法的类:


public class FinalMethodCallerClass {
// 持有一个包含FinalMethod的对象
private FinalMethodClass finalMethodClass;

public FinalMethodCallerClass (FinalMethodClass finalMethodClass) {
this.finalMethodClass = finalMethodClass;
}

public String callFinalMethod(){
return finalMethodClass.finalMethod();
}
}

测试示例代码如下:


@RunWith(PowerMockRunner.class)
@PrepareForTest({FinalMethodClass.class})
// 指明持有final方法的类
public class FinalMethodTest {

// 使用注解构建了mock对象,和直接使用PowerMockito.mock效果一样
@Mock
private FinalMethodClass finalMethodClass;

@Test
public void testCreateUser(){
String result = "this is mock finalMethod";
// 指定这个mock对象调用某个方法时的行为
PowerMockito.when (finalMethodClass.finalMethod()).thenReturn(result);
// 将mock对象传递给调用方,这里通过构造方法
FinalMethodCallerClass callerClass = new FinalMethodCallerClass (finalMethodClass);
// 调用这个调用方的调用方法
String mockResult = callerClass.callFinalMethod();
Assert.assertEquals (result,mockResult);
}
}

10. static方法的模拟

一个持有static方法的类:

<table width="70%" border="0" align="center" id="ccc" cellpadding="7" cellspacing="1" bgcolor="#CCCCCC" class="content" style="text-indent: 0em;">
<tr bgcolor="#FFFFFF">
<td height="25" bgcolor="#f5f5f5">aaa
</td>
</tr>
</table>

示例代码如下:


@RunWith(PowerMockRunner.class)
@PrepareForTest(StaticMethodClass.class)
// 指明持有static方法的类
public class StaticMethodTest {

@Test
public void test(){
// 和前面的区别,使用mockStatic方法
PowerMockito.mockStatic (StaticMethodClass.class);
// 指定static方法返回我们预期值
PowerMockito.when (StaticMethodClass.sayHello("hulu"))
.thenReturn("hello hulu, this is mock static method");
String result = StaticMethodClass.sayHello ("hulu");
Assert.assertEquals ("hello hulu, this is mock static method",result);
}
}

上述特殊之处在于需要在@PrepareForTest中指明持有static方法的类,同时在创建该类的mock对象时使用的是mockStatic方法。

11. private方法的模拟

一个持有private方法的类:

public class PrivateMethodClass {

public String callPrivate() {
System.out.println ("these is other method");
return privateMehod();
}

private String privateMehod() {
return "this is private mthod";
}

}

测试示例代码如下:

@RunWith (PowerMockRunner.class)
@PrepareForTest ({PrivateMethodClass.class})
public class PrivateMethodTest {

@Test
public void testMockPrivateFunc() throws Exception {
// spy一个PrivateMethodClass spy出来的对象可以认为是个真的对象
PrivateMethodClass privateMethodClass = PowerMockito.spy (new PrivateMethodClass());
// 指定这个对象中的private方法的返回值
String result = "this is mock private method";
PowerMockito.when (privateMethodClass, "privateMehod").thenReturn (result);
// 调用这个这个类中的调用private方法的返回值
String realResult = privateMethodClass.callPrivate();
Assert.assertEquals (realResult, result);
}
}

同样需要在@PrepareForTest指定持有private方法的类。

12. 级联方法的模拟

级联方法指的是形如a.method1().method2()的方法,关于这类方法的mock,思路就是一层一层进行模拟,具体例子可以参考redis部分和session部分,参考redisTemplate.opsForValue().get(key)和request.getSession().getAttribute("userId")的模拟。

13. 异常的模拟

以下例子为模拟调用某方法时,模拟方法返回异常,可以用来测试代码中异常处理机制是否满足需求。

public class ExceptionClass {

public void throwException (String name){
System.out.println("抛出异常");
}
}

测试示例代码如下:

@Test(expected = RuntimeException.class)
public void testException(){
// mock这个类
ExceptionClass mock = PowerMockito.mock (ExceptionClass.class);
// mock某个方法在指定参数下抛出异常
Exception e = new RuntimeException ("test");
PowerMockito.doThrow(e).when(mock).throwException ("mock");
// 执行当前行后就会抛出异常
String result = mock.throwException ("mock");
System.out.println("执行不到当前行代码");
}

上述描述mock异常的示例代码,可以根据实际需求进行异常的模拟,检测代码中异常处理机制。

14. 内部类的模拟

定义一个类Outer,持有两种内部类,分别是非静态和静态内部类。

public class Outer {
// 非静态内部类
public class Inner2{
String s = "hello hulu, this is inner method2.";
public String innerMethod (){
System.out.println ("inner2");
return s;
}
}
// 静态内部类
public static class Inner3{
static String s = "hello hulu, this is inner method3.";
public static String innerMethod(){
System.out.println ("inner3");
return s;
}
}
}

测试示例代码如下:

@RunWith(PowerMockRunner.class)
@PrepareForTest({Outer.Inner3.class})
// 静态内部类需要在这里指明
public class PowerMockInnerClassTest2 {
/**
* mock public修饰的非静态内部类
*/
@Test
public void InnerClassTest() {
// mock 普通成员变量内部类
Outer.Inner2 mockInner2 = PowerMockito.mock (Outer.Inner2.class);
PowerMockito.when (mockInner2.innerMethod()).thenReturn("this is mock inner method2.");
String result = mockInner2.innerMethod();
Assert.assertEquals ("this is mock inner method2.",result);
}
/**
* mock public修饰的静态内部类
*/
@Test
public void InnerClassTest2() {
// mock static 的内部类
PowerMockito.mockStatic(Outer.Inner3.class);
PowerMockito.when (Outer.Inner3.innerMethod()).thenReturn ("this is mock inner method3.");
String result = Outer.Inner3.innerMethod ();
Assert.assertEquals ("this is mock inner method3.",result);
}
}

15. 单例的模拟

这里使用单例模式的最好实践,静态内部类的单例,懒加载,并且单例用类加载机制保证,线程安全。代码如下:

public class Singleton {

private Singleton (){};

public String printHelloWorld ( String value ) {
StringBuilder stringBuilder = new StringBuilder ( "name is: " );
return stringBuilder.append ( value ).toString();
}

/**
* 内部类持有外部类的一个对象,类加载时候不会初始化,只有真正被调用时候才会被初始化
*/
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton ();
}

public static Singleton getInstance () {
return SingletonInstance.INSTANCE;
}
}

创建一个单例类的使用者,调用了单例的方法:

public class SingletonUser {

public String useSingletonMethod(){
String result = Singleton.getInstance().printHelloWorld ("hulu");
return result;
}
}

单元测试示例代码如下:

@RunWith(PowerMockRunner.class)
@PrepareForTest(Singleton.class)
// 特殊类 需要使用这个注解进行准备
public class SingletonTest {

/**
* mock一个单例,并且模拟单例的方法返回值
* @throws ClassNotFoundException
*/
@Test
public void test() throws ClassNotFoundException {
// mock一个单例类
Singleton mockSingleton = PowerMockito.mock(Singleton.class);
// 通过反射获取这个单例类内部的内部类的class对象
Class clazz = Whitebox.getInnerClassType(Singleton.class, "SingletonInstance");
// 通过反射给内部类持有的外部类的引用赋值为mock出的单例对象
Whitebox.setInternalState(clazz, "INSTANCE", mockSingleton);
// 当调用单例类的方法时候 指定其返回值
PowerMockito.when( mockSingleton.printHelloWorld (Mockito.anyString())).thenReturn( "this is mock singleton method result" );
// 创建单例类调用者
SingletonUser singletonUser = new SingletonUser();
// 单例类方法调用者调用方法
String result = singletonUser.useSingletonMethod();
Assert.assertEquals("this is mock singleton method record",singletonUser.useSingletonMethod());
}
}

上述代码中特殊之处在于,持有单例的内部类是private的,因此在获取内部类的class对象时,需要使用PowerMock提供的反射工具类,通过反射的方式获取其class对象,然后通过反射给内部类的属性指定我们mock出的单例,并且指定单例在执行本身printHelloWorld()方法时返回我们预期的值,进而完成单例对象方法调用的模拟。

上述介绍的场景其实有很多在实际单元测试过程中是很少遇到的,通常mock框架使用最多的场景是前面介绍的第三方依赖及复杂对象的场景。

三、总结

以上展示了PowerMock如何解决项目中一些强的第三方依赖、复杂对象及特殊方法等测试场景:使用mock框架创建虚拟的对象,指定相应对象方法调用时的返回值,从而达到调用对应代码时,能够按照预期返回指定值,进而保证了测试方法中其他业务逻辑的正常执行;由于屏蔽了外部依赖,简化了单元测试所需真实环境的配置,提高了单元测试的执行效率,为软件的基本组成单元的正确性提供了保证。

   
次浏览       
相关文章

微服务测试之单元测试
一篇图文带你了解白盒测试用例设计方法
全面的质量保障体系之回归测试策略
人工智能自动化测试探索
相关文档

自动化接口测试实践之路
jenkins持续集成测试
性能测试诊断分析与优化
性能测试实例
相关课程

持续集成测试最佳实践
自动化测试体系建设与最佳实践
测试架构的构建与应用实践
DevOps时代的测试技术与最佳实践
最新活动计划
LLM大模型应用与项目构建 12-26[特惠]
QT应用开发 11-21[线上]
C++高级编程 11-27[北京]
业务建模&领域驱动设计 11-15[北京]
用户研究与用户建模 11-21[北京]
SysML和EA进行系统设计建模 11-28[北京]
 
最新文章
大数据平台测试
微服务架构下的测试之道
从零开始掌握微服务软件测试
如何进行测试需求分析:从接收需求到用例设计
python_selenium自动化测试框架
最新课程
测试需求分析与测试用例设计
性能测试方法与技术
自动化测试框架设计高级实践
接口自动化测试方法与工具
软件测试方法与实践(贯穿案例)
更多...   
成功案例
某支付企业 单元测试与重构培训
北京 用户体验、可用性测试与评估
某军工研究单位 自动化测试方法、案例与工具
知名消费金融公司 探索性测试与测试分析
北京 航天科工某子公司 软件测试架构师
更多...