我有这个测试:
@RunWith(MockitoJUnitRunner.class)
public class myServiceTest {
@InjectMocks
myService subject;
private myService spy;
@Before
public void before() {
spy = spy(subject);
}
@Test
public void testing() {
when(spy.print2()).thenThrow(new RuntimeException()).thenThrow(new RuntimeException()).thenReturn("completed");
spy.print1();
verify(spy, times(3)).print2();
}
然后我有:
@Service("myService")
public class myService extends myAbstractServiceClass {
public String print1() {
String temp = "";
temp = print2();
return temp;
}
@Retryable
public String print2() {
return "completed";
}
}
然后我有这个接口(我的abstractService实现):
public interface myServiceInterface {
@Retryable(maxAttempts = 3)
String print1() throws RuntimeException;
@Retryable(maxAttempts = 3)
String print2() throws RuntimeException;
}
但是,当我运行测试时,我得到一个runtimeexception,导致我认为它没有重试。我做错了吗?
答案 0 :(得分:6)
这是因为您没有使用SpringJUnitClassRunner
。
Mockito和您自己的课程没有考虑@Retryable
注释。所以你依靠Spring的实现来做到这一点。但是你的测试没有激活Spring。
这是来自SpringJUnit4ClassRunner JavaDoc:
SpringJUnit4ClassRunner是JUnit的BlockJUnit4ClassRunner的自定义扩展,它通过TestContextManager和相关的支持类和注释为标准JUnit测试提供Spring TestContext Framework的功能。 要使用此类,只需使用@RunWith(SpringJUnit4ClassRunner.class)或@RunWith(SpringRunner.class)注释基于JUnit 4的测试类。
您应该至少将测试类重组为以下内容:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=MyConfig.class)
public class MyServiceTest {
@Configuration
@EnableRetry
@Import(myService.class)
public static class MyConfig {}
...
我在那里做什么?
还有其他一些陷阱吗?
test -> retryableService -> exceptionThrowingBean
这样的结构。然后你可以使用Springockito或任何你喜欢的东西,例如ReflectionTestUtils
使用您喜欢的行为配置exceptionThrowingBean
。MyServiceInterface
first letter of each internal word capitalized
希望有所帮助。
答案 1 :(得分:2)
我认为你应该让Spring管理bean,创建适当的代理并处理这个过程。 如果要模拟特定的bean,可以创建模拟并将它们注入到测试服务中。
第一个选项可能是展开代理服务,创建模拟并手动注入它们:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {RetryConfiguration.class})
@DirtiesContext
public class TheServiceImplTest {
@Autowired
private TheService theService;
@Before
public void setUp(){
TheService serviceWithoutProxy = AopTestUtils.getUltimateTargetObject(theService);
RetryProperties mockRetryProperties = Mockito.mock(RetryProperties.class);
ReflectionTestUtils.setField(serviceWithoutProxy, "retryProperties", mockRetryProperties);
}
@Test
public void shouldFetch() {
Assert.assertNotNull(theService);
}
}
在这个例子中,我模拟了一个bean,RetryProperties,并注入到服务中。另请注意,在此方法中,您正在修改由Spring缓存的测试应用程序上下文。这意味着如果你不使用@DirtiesContext,服务将在其他测试中继续使用mocked bean。您可以阅读更多here
第二个选项是创建一个特定于测试的@Configuration并在那里模拟依赖的bean。 Spring会选择这个新的模拟bean而不是原始的bean:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {RetryConfiguration.class, TheServiceImplSecondTest.TestConfiguration.class})
public class TheServiceImplSecondTest {
@Autowired
private TheService theService;
@Test
public void shouldFetch() {
Assert.assertNotNull(theService);
}
@Configuration
static class TestConfiguration {
@Bean
public RetryProperties retryProperties() {
return Mockito.mock(RetryProperties.class);
}
}
}
在此示例中,我们定义了一个特定于测试的配置,并将其添加到@ContextConfiguration。
答案 2 :(得分:0)
另一种方式:
@EnableRetry
@RunWith(SpringRunner.class)
@SpringBootTest(classes={ServiceToTest.class})
public class RetryableTest {
@Autowired
private ServiceToTest serviceToTest;
@MockBean
private ComponentInsideTestClass componentInsideTestClass;
@Test
public void retryableTest(){
serviceToTest.method();
}
}