Spring Boot、Dubbo项目Mock测试踩坑与总结

简介: 本文是对Spring Boot、Dubbo项目进行Mock测试的总结与踩坑实录。搜索了一圈,居然没发现类似的文章,莫非用Dubbo的朋友们都不Mock测试,或者有其他的办法测试吗?简单总结了一下,希望对大家能有一定参考意义。

本文是对Spring Boot、Dubbo项目进行Mock测试的总结与踩坑实录。

搜索了一圈,居然没发现类似的文章,莫非用Dubbo的朋友们都不Mock测试,或者有其他的办法测试吗?

简单总结了一下,希望对大家能有一定参考意义。如果有更好的测试方法,请联系我的邮箱eacdy0000@126.com ,帮忙告知一下。

背景

手上有个整合了Dubbo的Spring Boot应用,在应用中需要消费其他服务的API。由于我依赖的服务并不由我所在的项目组维护(对方可能接口中途会发生变化,甚至,有时候可能并未启动)。

集成测试成本略高,故而想办法Mock测试。以RemoteApi为例,这是一个远程的API。我这一侧(消费者)的代码如下:

@Service
public class MyApi {
    @Reference
    private RemoteApi remoteApi;

    public String hold() {
        return remoteApi.hold();
    }
}

由代码可知,MyApi调用了一个远程的API RemoteApi。

下面我们来mock测试。

整合powermock

经过调研,笔者选择powermock作为项目的mock工具。

  1. 加依赖:

    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>1.6.6</version>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito</artifactId>
        <version>1.6.6</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4-rule</artifactId>
        <version>1.6.6</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-classloading-xstream</artifactId>
        <version>1.6.6</version>
        <scope>test</scope>
    </dependency>
    
  2. 测试启动类:

    @SpringBootApplication
    public class ConsumerTest {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerTest.class, args);
        }
    
        @Bean
        public RemoteApi RemoteApi() {
            RemoteApi remoteApi = PowerMockito.mock(RemoteApi.class);
            PowerMockito.when(remoteApi.hold())
                    .thenAnswer(t -> "我是Mock的API。");
            return remoteApi;
        }
    }
    

    由代码可知,我在这里mock了一个RemoteApi,当调用Mock的RemoteApi.hold()方法时,返回

    我是Mock的API。
    

  3. 测试类:

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringBootTest(classes = ConsumerTest.class)
    public class MyApiTest {
        @Autowired
        public MyApi myApi;
    
        @Test
        public void hold() {
            Assert.assertEquals("我是Mock的API。", this.myApi.hold());
        }
    }
    
  4. 执行单元测试,发现Mock并没有成功,Dubbo依然会尝试调用远程API,而并非笔者Mock的RemoteApi。

分析

Mock没有成功,为什么呢?我们不妨将测试类代码修改成如下:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ConsumerTest.class)
public class MyApiTest {
    @Autowired
    private ApplicationContext applicationContext;

    @Before
    public void before() {
        MyApi myApi = applicationContext.getBean(MyApi.class);
        RemoteApi fromMyApi = myApi.getRemoteApi();
        RemoteApi fromSpring = applicationContext.getBean(RemoteApi.class);
        System.out.println("MyApi中注入的RemoteApi是:" + fromMyApi);
        System.out.println("Spring容器中注入的RemoteApi是:" + fromSpring);
    }

    @Autowired
    public MyApi myApi;

    @Test
    public void hold() {
        Assert.assertEquals("我是Mock的API。", this.myApi.hold());
    }
}

从代码不难发现,我在执行单元测试之前,分别打印MyApi对象中注入的RemoteApi,以及Spring容器中的RemoteApi(@Bean所注解的方法,new出来的对象,必然在Spring容器中)。

执行后,打印结果如下:

MyApi中注入的RemoteApi是:com.alibaba.dubbo.common.bytecode.proxy0@541afb85
Spring容器中注入的RemoteApi是:remoteApi

由打印结果可知,MyApi中注入的RemoteApi和容器中的RemoteApi,压根不是一个实例。

由该结果,可以知道2点:

  1. Dubbo的@Reference注解拿到的一个代理;
  2. @Reference生成的代理并不在Spring容器中(如果Dubbo的Reference的代理也是容器中,那么容器中应该有2个RemoteApi实例,那么调用getBean()应当报错);

解决

原因我们知道了,要如何解决呢?答案很简单——如果我们在执行单元测试之前,将StoreApi中注入的RemoteApi换成Spring容器中的实例(即我们Mock的那个对象),那么问题就可以得到就解决。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ConsumerTest.class)
public class MyApiTest {
    @Autowired
    private ApplicationContext applicationContext;

    @Before
    public void before() {
        MyApi myApi = applicationContext.getBean(MyApi.class);
        RemoteApi fromSpring = applicationContext.getBean(RemoteApi.class);
        myApi.setRemoteApi(fromSpring);
    }

    @Autowired
    public MyApi myApi;

    @Test
    public void hold() {
        Assert.assertEquals("我是Mock的API。", this.myApi.hold());
    }
}

再次执行,就会发现,现在已经可以正常Mock了。

源码分析

以上已经提供了解决方案。那么,@Reference注解究竟干了哪些事情呢?我们不妨分析一下。搜索@Reference注解被哪些地方使用,可找到以下代码:com.alibaba.dubbo.config.spring.AnnotationBean#postProcessBeforeInitialization 。以该代码是我们定位问题的入口,由此,我们可以定位到以下两个方法:

  1. com.alibaba.dubbo.config.ReferenceConfig#init
  2. com.alibaba.dubbo.config.ReferenceConfig#createProxy

其中,

  1. createProxy方法用于创建代理对象;

  2. init方法用来判断是否已经初始化,如果没有初始化,就会调用createProxy创建代理对象。过程比较简单,不贴了。

了解Dubbo如何创建对象后,我们来看看Dubbo是如何将代理对象设置到MyApi的,如下图。

dubbo-source.png

分析至此,大家应该能够了解原因了——

  1. @Reference创建了一个代理;
  2. Dubbo自身做了一些判断,如果发现没有初始化,就会创建一个代理;
  3. postProcessBeforeInitialization 方法中,从Spring容器中拿到MyApi对象,并将这个代理对象设到MyApi实例中。

Going Far

我们已经知道,是@Reference注解搞的鬼,除了以上解决方案,还可以弄一个类,转一下。

即:原调用链:

MyApi —> RemoteApi

改为:

MyApi —> 一个转换的类,啥都不干,用了@Service注解,在里面调用RemoteApi的方法 —> RemoteApi

WHATS MORE OVER

如果使用xml配置,不存在该问题,可以很简单地Mock。

配套代码

https://github.com/itmuch/spring-boot-dubbo-mock-sample

本文链接: http://www.itmuch.com/dubbo/spring-boot-dubbo-mock/
**版权声明: **本博客由周立创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。

目录
相关文章
|
4月前
|
人工智能 测试技术 项目管理
测试不再碎片化:AI智能体平台「项目资料套件」功能上线!
在实际项目中,需求文档分散、整理费时、测试遗漏等问题常困扰测试工作。霍格沃兹推出AI智能体测试平台全新功能——项目资料套件,可将多个关联文档打包管理,并一键生成测试用例,提升测试完整性与效率。支持套件创建、文档关联、编辑删除及用例生成,适用于复杂项目、版本迭代等场景,助力实现智能化测试协作,让测试更高效、更专业。
|
3月前
|
Java 测试技术 数据库连接
【SpringBoot(四)】还不懂文件上传?JUnit使用?本文带你了解SpringBoot的文件上传、异常处理、组件注入等知识!并且带你领悟JUnit单元测试的使用!
Spring专栏第四章,本文带你上手 SpringBoot 的文件上传、异常处理、组件注入等功能 并且为你演示Junit5的基础上手体验
907 2
|
4月前
|
测试技术 UED 开发者
性能测试报告-用于项目的性能验证、性能调优、发现性能缺陷等应用场景
性能测试报告用于评估系统性能、稳定性和安全性,涵盖测试环境、方法、指标分析及缺陷优化建议,是保障软件质量与用户体验的关键文档。
|
6月前
|
Java 关系型数据库 数据库连接
Spring Boot项目集成MyBatis Plus操作PostgreSQL全解析
集成 Spring Boot、PostgreSQL 和 MyBatis Plus 的步骤与 MyBatis 类似,只不过在 MyBatis Plus 中提供了更多的便利功能,如自动生成 SQL、分页查询、Wrapper 查询等。
571 3
|
6月前
|
Java 测试技术 Spring
简单学Spring Boot | 博客项目的测试
本内容介绍了基于Spring Boot的博客项目测试实践,重点在于通过测试驱动开发(TDD)优化服务层代码,提升代码质量和功能可靠性。案例详细展示了如何为PostService类编写测试用例、运行测试并根据反馈优化功能代码,包括两次优化过程。通过TDD流程,确保每项功能经过严格验证,增强代码可维护性与系统稳定性。
279 0
|
6月前
|
存储 Java 数据库连接
简单学Spring Boot | 博客项目的三层架构重构
本案例通过采用三层架构(数据访问层、业务逻辑层、表现层)重构项目,解决了集中式开发导致的代码臃肿问题。各层职责清晰,结合依赖注入实现解耦,提升了系统的可维护性、可测试性和可扩展性,为后续接入真实数据库奠定基础。
517 0
|
6月前
|
Java 应用服务中间件 Maven
第01课:Spring Boot开发环境搭建和项目启动
第01课:Spring Boot开发环境搭建和项目启动
1093 0
|
6月前
|
人工智能 数据可视化 测试技术
UAT测试排程工具深度解析:让验收测试不再失控,项目稳稳上线
在系统交付节奏加快的背景下,“测试节奏混乱”已成为项目延期的主因之一。UAT测试排程工具应运而生,帮助团队结构化拆解任务、清晰分配责任、实时掌控进度,打通需求、测试、开发三方协作闭环,提升测试效率与质量。本文还盘点了2025年热门UAT工具,助力团队选型落地,告别靠表格和群聊推进测试的低效方式,实现有节奏、有章法的测试管理。
|
Dubbo Java 应用服务中间件
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
|
Dubbo Java 应用服务中间件
Spring Cloud Dubbo:微服务通信的高效解决方案
【10月更文挑战第15天】随着信息技术的发展,微服务架构成为企业应用开发的主流。Spring Cloud Dubbo结合了Dubbo的高性能RPC和Spring Cloud的生态系统,提供高效、稳定的微服务通信解决方案。它支持多种通信协议,具备服务注册与发现、负载均衡及容错机制,简化了服务调用的复杂性,使开发者能更专注于业务逻辑的实现。
291 2

热门文章

最新文章