我搜索了SO并发现了许多看似相似但不完全正确的问题,所以我会问另一个问题。
我有Spring应用程序,并说我创建了自定义方面(查找CatchMe
注释)以特定方式记录异常。我想通过模拟我的一个Spring @Service类的方法的行为来测试方面,因此它在调用时抛出异常。然后在另一个用我的自定义注释@CatchMe
注释的方法中,我调用了第一个方法。我期望发生的是记录的异常。不幸的是抛出了异常,但没有触发方面。那么如何使用Mockito在这个测试中触发方面?
注意:我已经检查了这些(加上更多):
但是大多数都是与控制器相关的,而不是服务相关的,我只想测试服务。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {BeanConfig.class})
public class MyServiceTest {
@Autowired
@InjectMocks
private MyService service;
@Mock
private MyServiceDependency serviceDep;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(service, "serviceDep", serviceDep);
}
@Test
public void test() {
when(serviceDep.process()).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
throw new Exception("Sample message.");
}
});
service.execute();
}
}
@Service
public class MyService {
@Autowired
private MyServiceDependency serviceDep;
@CatchMe
public void execute() {
serviceDep.process();
}
}
@Service
public class MyServiceDependency {
public Object process() {
// may throw exception here
}
}
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"com.example.services"})
public class BeanConfig { .. }
@Aspect
@Component
public class CatchMeAspect {
@Around("@annotation(CatchMe)")
public Object catchMe(final ProceedingJoinPoint pjp) throws Throwable {
try {
pjp.proceed();
} catch (Throwable t) {
// fency log
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CatchMe {}
编辑:该功能有效,但我想通过测试验证它。
答案 0 :(得分:1)
实际上它正在按预期工作,但是你运行的是基于代理的AOP的副作用,特别是在这种情况下基于类的代理。
目前,您在代理上设置字段,而不是在代理中的实际对象上设置字段。这是你真正想要的。要获取实际实例,请使用AopTestUtils.getUltimateTargetObject
方法,然后在ReflectionTestUtils.setField
方法中使用它。
@Autowired
@InjectMocks
private MyService service;
@Mock
private MyServiceDependency serviceDep;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
MyService serviceToInject = AopTestUtils.getUltimateTargetObject(service);
ReflectionTestUtils.setField(serviceToInject, "serviceDep", serviceDep);
}
但是我认为这种方法是错误的,当你开始乱搞时,有一种更好的方法。只需使用Spring注入模拟。为此测试用例创建一个特定的@Configuration
类。将其设为内部public static class
,并为依赖项添加模拟@Bean
。
@Configuration
@Import(BeanConfig.class)
public static class TestBeanConfig {
@Bean
public MyServiceDependency myServiceDependency() {
return Mockito.mock(MyServiceDependency.class);
}
}
现在在您的测试类中,您可以简单地@Autowire
两个bean,而不需要使用反射或其他任何设置依赖项。
@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
@Autowired
private MyService service;
@Autowired
private MyServiceDependency serviceDep;
@Test
public void test() {
when(serviceDep.process()).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
throw new Exception("Sample message.");
}
});
service.execute();
}
}
将负责正确的依赖关系。
答案 1 :(得分:0)
我遇到了与@nyxz相同的问题,这是有意的,请参见https://github.com/spring-projects/spring-boot/issues/7243。
受@M启发。 Deinum以下解决方案适用于Spring Boot 2.3.4.RELEASE和JUnit 5。
我们将只提供不含@MockedBean
@SpringBootTest
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class MyServiceTest {
@Autowired
private MyService service;
@Test
public void test() {
service.execute();
}
static class TestBeanConfig {
@Bean
@Primary
public MyServiceDependency myServiceDependency() {
MyServiceDependency myServiceDependency = Mockito.mock(MyServiceDependency.class)
// Add behavior of mocked bean here
return myServiceDependency;
}
}
}