Dubbo是我们常用的RPC框架,在写单元测试需要调用Dubbo消费Bean时,如何模拟Dubbo消费Bean的行为呢?
就拿发邮件来说,通常,在代码中,我们是调用邮件的Dubbo服务来完成发送邮件的目的,于是我们会在Spring配置好的发邮件的Dubbo消费Bean,
dubbo-consumer.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<?
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:dubbo
=
"http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<
dubbo:application
name
=
"consumer-of-dubbo-mock-test"
/>
<
dubbo:registry
address
=
"zookeeper://your-zk-address:2181"
/>
<!-- 生成远程服务代理,可以和本地bean一样使用mailService -->
<
dubbo:reference
id
=
"mailService"
interface
=
"cn.jmockit.demos.MailService"
/>
</
beans
>
|
熟悉Dubbo的朋友都知道,上面xml配置是Dubbo的基本配置,配置了dubbo服务的zookeeper地址,还配置了名叫mailService的Dubbo消费Bean,用于在应用程序中发送邮件。
我们在运行单元测试时,如果zookeeper连不上或者mailService的服务提供者不存在,则会导致Spring初始化失败, 而且我们也不希望真正发送邮件(除非是为了测试发送邮件)。于是我们希望对mailService进行Mock。
下面给出一种Mock Dubbo消费Bean的方案:
-
在spring初始化前,对所有Dubbo消费Bean的进行Mock,即<dubbo>标签里的interface都返回本地默认实现。
-
如果想对某几个Dubbo消费Bean进行Mock,则自定义Dubbo消费Bean的实现即可。
请看测试代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
//dubbo消费bean Mock
@SuppressWarnings
({
"unchecked"
,
"rawtypes"
})
@ContextConfiguration
(locations = {
"/META-INF/dubbo-consumer.xml"
})
@RunWith
(SpringJUnit4ClassRunner.
class
)
public
class
DubboConsumerBeanMockingTest {
// 这里要@BeforeClass,因为要抢在spring加载dubbo前,对dubbo的消费工厂bean
// ReferenceBean进行mock,不然dubbo可能因为连上不zk或无法找不
// 服务的提供者等原因而无法初始化的,进而,单元测试运行不下去
@BeforeClass
public
static
void
mockDubbo() {
// 你准备mock哪个消费bean
// 比如要对dubbo-consumber.xml里配置的cn.jmockit.demos.MailService这个消费bean进行mock
Map<String, Object> mockMap =
new
HashMap<String, Object>();
mockMap.put(
"cn.jmockit.demos.MailService"
,
new
MockUp(MailService.
class
) {
// 在这里书写对这个消费bean进行mock的mock逻辑,想mock哪个方法,就自行添加,注意方法一定要加上@Mock注解哦
@Mock
public
boolean
sendMail(
long
userId, String content) {
// 单元测试时,不需要调用邮件服务器发送邮件,这里统一mock邮件发送成功
return
true
;
}
}.getMockInstance());
// 如果要Mock其它的消费bean,自行添加,mockMap.put.....如上
new
DubboConsumerBeanMockUp(mockMap);
}
// 现在你使用的dubbo消费bean就是本地mock过的了,并不是指向远程dubbo服务的bean了
@Resource
MailService mailService;
@Test
public
void
testSendMail() {
long
userId =
123456
;
String content =
"test mail content"
;
Assert.isTrue(mailService.sendMail(userId, content));
}
}
|
上述代码,最关键的就是DubboConsumerBeanMockUp类了,这个类Mock了所有的Dubbo消费Bean.
源代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
//dubbo消费bean的MockUp(伪类)
@SuppressWarnings
(
"rawtypes"
)
public
class
DubboConsumerBeanMockUp
extends
MockUp<ReferenceBean> {
// 自定义的消费bean mock对象
private
Map<String, Object> mockMap;
public
DubboConsumerBeanMockUp() {
}
public
DubboConsumerBeanMockUp(Map<String, Object> mockMap) {
this
.mockMap = mockMap;
}
// 对ReferenceBean的getObject方法的Mock
@SuppressWarnings
(
"unchecked"
)
@Mock
public
Object getObject(Invocation inv)
throws
Exception {
ReferenceBean ref = inv.getInvokedInstance();
String interfaceName = ref.getInterface();
Object mock = mockMap.get(interfaceName);
if
(mock !=
null
) {
return
mock;
}
return
(
new
MockUp(Class.forName(interfaceName)) {
}).getMockInstance();
}
}
|
© JMockit中文网 2018 打赏咖啡