Java的Sevice层会有很多void类型的方法,比如save*、update*,这类方法只是做一些更新,不会有返回值,其单测不能根据方法的返回值来编写,只能采用特殊方法;
本方法环境:Mockito、testng
被测试的方法:
@Override public void updateRuleName(Long ruleId, String newRuleName, Long ucId) { Assert.notNull(ruleId, "规则ID不能为Null"); Assert.notNull(newRuleName, "规则名称不能为Null"); Assert.notNull(ucId, "操作人的UCID不能为Null"); String cleanNewRuleName = StringUtils.trim(newRuleName); if (StringUtils.isBlank(cleanNewRuleName)) { throw new IllegalArgumentException("新的规则名称不能为空"); } // 查询规则对象 Rule rule = queryRuleById(ruleId); if (null == rule) { throw new IllegalDataException("没有查到该规则"); } rule.setRuleId(ruleId); rule.setRuleName(cleanNewRuleName); rule.setUpdateUcid(ucId); rule.setUpdateTime(new Date()); ruleDao.updateSelective(rule); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@Override
public
void
updateRuleName
(
Long
ruleId
,
String
newRuleName
,
Long
ucId
)
{
Assert
.
notNull
(
ruleId
,
"规则ID不能为Null"
)
;
Assert
.
notNull
(
newRuleName
,
"规则名称不能为Null"
)
;
Assert
.
notNull
(
ucId
,
"操作人的UCID不能为Null"
)
;
String
cleanNewRuleName
=
StringUtils
.
trim
(
newRuleName
)
;
if
(
StringUtils
.
isBlank
(
cleanNewRuleName
)
)
{
throw
new
IllegalArgumentException
(
"新的规则名称不能为空"
)
;
}
// 查询规则对象
Rule
rule
=
queryRuleById
(
ruleId
)
;
if
(
null
==
rule
)
{
throw
new
IllegalDataException
(
"没有查到该规则"
)
;
}
rule
.
setRuleId
(
ruleId
)
;
rule
.
setRuleName
(
cleanNewRuleName
)
;
rule
.
setUpdateUcid
(
ucId
)
;
rule
.
setUpdateTime
(
new
Date
(
)
)
;
ruleDao
.
updateSelective
(
rule
)
;
}
|
测试的方法:
@Test public void testUpdateRuleName() { Long ruleId = 1L; String newRuleName = "newRuleName"; Long ucId = 123L; List rules = new ArrayList(); Rule rule = new Rule(); rule.setRuleStatus((byte) DBValueSetting.RULE_STATUS_TAKE_EFFECT); rules.add(rule); // 查询规则对象 Map params = new HashMap(); params.put("ruleId", ruleId); Mockito.when(ruleDao.queryRulesByCondition(params)).thenReturn(rules); Mockito.doAnswer(new Answer
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
|
@Test
public
void
testUpdateRuleName
(
)
{
Long
ruleId
=
1L
;
String
newRuleName
=
"newRuleName"
;
Long
ucId
=
123L
;
List
Rule
rule
=
new
Rule
(
)
;
rule
.
setRuleStatus
(
(
byte
)
DBValueSetting
.
RULE_STATUS_TAKE_EFFECT
)
;
rules
.
add
(
rule
)
;
// 查询规则对象
Map
<
String
,
Object
>
params
=
new
HashMap
<
String
,
Object
>
(
)
;
params
.
put
(
"ruleId"
,
ruleId
)
;
Mockito
.
when
(
ruleDao
.
queryRulesByCondition
(
params
)
)
.
thenReturn
(
rules
)
;
Mockito
.
doAnswer
(
new
Answer
(
)
{
public
Object
answer
(
InvocationOnMock
invocation
)
{
// 断点2:这里随后执行
Rule
rule
=
(
Rule
)
invocation
.
getArguments
(
)
[
0
]
;
Assert
.
assertTrue
(
rule
.
getRuleName
(
)
.
equals
(
"newRuleName"
)
)
;
return
null
;
}
}
)
.
when
(
ruleDao
)
.
updateSelective
(
Mockito
.
any
(
Rule
.
class
)
)
;
// 断点1:先执行到这里
ruleService
.
updateRuleName
(
ruleId
,
newRuleName
,
ucId
)
;
}
|
如注释所示,如果加了两个断点的话,执行的过程中,会先执行最后的调用行,端点1执行的过程中,会执行到端点2的stub,这时候在断点2可以获取到方法执行的入参,对入参进行Assert校验,即可实现目的;
new Anwer是个接口,其中只有一个方法,用于设置方法调用的代理执行入口
public interface Answer { /** * @param invocation the invocation on the mock. * * @return the value to be returned * * @throws Throwable the throwable to be thrown */ T answer(InvocationOnMock invocation) throws Throwable; }
1
2
3
4
5
6
7
8
9
10
|
public
interface
Answer
/**
* @param invocation the invocation on the mock.
*
* @return the value to be returned
*
* @throws Throwable the throwable to be thrown
*/
T
answer
(
InvocationOnMock
invocation
)
throws
Throwable
;
}
|
当代码执行到“ruleDao.updateSelective(rule);”的时候,会触发针对mock对象调用的拦截器,在拦截器中,会创建一个动态代理,动态代理的invocation就是new Answer中覆盖的方法;
使用拦截、代理两种方法,实现了对mock对象方法的入参、出参的设定和获取,使用这种方式,就可以校验VOID方法内部的执行类调用的情况;