因此,我在项目中使用Spring=Retry
库,并尝试为此运行测试用例。
我正在将服务DCS加载为返回新对象的bean。
它的两个依赖项SSService和AttributeService也作为bean加载。但是这两个是模拟的。
当我的测试规范运行时,我可以在DCS.execute中看到模拟程序在那里。但是像1 * SSService.read(_ as LComponent,_) >> mockSimpleSettingCommResult
这样的交互不会生效,导致返回空值而不是我希望它返回的值。
@ContextConfiguration(classes = [SpringRetryConfig])
class DCSISpec extends Specification {
@Autowired
DCS DCS
@Autowired
SSService sSService
@Autowired
AttributeService attributeService
def setup() {
// DCS.SSService = SSService
// DCS.attributeService = attributeService
}
def "execute failure"(){
setup:
DataCollectionDataSet mockDataCollectionDataSet = Mock(DataCollectionDataSet)
LComponent mockLComponent = Mock(LComponent)
SSCommResult mockSimpleSettingCommResult = Mock(SSCommResult)
ReflectionTestUtils.setField(DCS, "SSService", SSService)
ReflectionTestUtils.setField(DCS, "attributeService", attributeService)
when:
DCS.execute(mockLComponent, mockDataCollectionDataSet)
then:
1 * mockSimpleSettingCommResult.getDegreeOfSuccess() >> SSCommResult.DegreeOfSuccess.FAILURE
1 * mockDataCollectionDataSet.getNamespace() >> DCSNamespace.xyz
1 * mockDataCollectionDataSet.getDataElements() >> ["FOO": "BAR"]
1 * SSService.read(_ as LComponent,_) >> mockSimpleSettingCommResult
3 * DCS.execute(_ as LComponent, _ as DataCollectionDataSet)
}
@Configuration
@EnableRetry
public static class SpringRetryConfig {
@Bean
public SSService SSService() {
Mockito.mock(SSService)
}
@Bean
public AttributeService attributeService() {
Mockito.mock(AttributeService)
}
@Bean
public DCS DCS() {
return new DCS();
}
}
}
这是我尝试使用普通Mockito时遇到的异常,
java.lang.NullPointerException
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:41)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:136)
at com.lexmark.mps.cma.service.DataCollectionRetryTest.test_retry(DataCollectionRetryTest.groovy:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:73)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:224)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
答案 0 :(得分:1)
鉴于我对上述问题的评论,我认为您最好在此处使用裸露的Mockito(因为您正在为如何为该用例制定可读的Spock规范而在您的评论中不带Spock) :
编辑:完全正确的解决方案在此解决方案之下
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DCSTest.SpringRetryConfig.class)
public class DCSTest {
@Autowired
private DCS dcs;
@Test
public void test_retry() {
//given:
LComponent component = mock(LComponent.class);
DataCollectionDataSet dataSet = mock(DataCollectionDataSet.class);
given(dcs.execute(component, dataSet)) //It's BDDMockito class
.willThrow(new RuntimeException("1"))
.willThrow(new RuntimeException("2"))
.willReturn("Foo");
//when:
String result = dcs.execute(component, dataSet);
//then:
verify(dcs, times(3)).execute(component, dataSet);
assertThat(result, equalTo("Foo"));
}
@Configuration
@EnableRetry
static class SpringRetryConfig {
@Bean
DCS dcs() {
return mock(DCS.class);
}
}
}
public class DCS {
@Retryable(maxAttempts = 3)
String execute(LComponent component, DataCollectionDataSet dataSet) {
return "Bar";
}
}
正确的解决方案:
好吧,作为OP并发现以上verify(dcs, times(3)).execute(component, dataSet);
不能正常工作。无论您在times()
中使用什么数字,测试都将始终成功。这是因为@Retryable
围绕dcs
模拟创建了一个方面。结果,每次对dcs.execute
的调用都会被Spring拦截,而Mockito并不会真正验证该调用。为了克服这个问题,我们可以围绕模拟创建自己的方面,并且作为副作用,计算@Retryable
方法被调用的次数。下面是这种解决方案的工作代码:
@RunWith(SpringJUnit4ClassRunner.class)
public class DCSTest {
@Autowired
private LComponent component;
@Autowired
private DataCollectionDataSet dataSet;
@Autowired
private DCS dcs;
@Autowired
private RetryCount retryCount;
@Test
public void test_retry() {
//when:
String result = dcs.execute(component, dataSet);
//then:
assertThat(retryCount.value, equalTo(3));
assertThat(result, equalTo("Foo"));
}
@Aspect
public static class RetryCount {
public int value = 0;
@Before("execution(* DCS.execute(..))")
public void advice() {
value++;
}
}
@Configuration
@EnableRetry
@EnableAspectJAutoProxy
public static class SpringRetryConfig {
@Bean
DCS dcs() {
DCS dcs = mock(DCS.class);
given(dcs.execute(component(), dataSet())) //It's BDDMockito class and take note that better to keep this declaration here so that Spring doesn't intercept the call once Retryable aspect is created
.willThrow(new RuntimeException("1"))
.willThrow(new RuntimeException("2"))
.willReturn("Foo");
return dcs;
}
@Bean
RetryCount retryCount() {
return new RetryCount();
}
@Bean
LComponent component() {
return new LComponent();
}
@Bean
DataCollectionDataSet dataSet() {
return new DataCollectionDataSet();
}
}
}