我搜索了SO,发现了一堆其他看起来相似但不完全一样的问题,所以我会问另一个问题。
我有 Spring 应用程序,并说我创建了自定义方面(寻找CatchMe
注释(以特定方式记录异常。我想通过模拟我的 Spring @Service 类方法之一的行为来测试方面,以便在调用时抛出异常。然后在另一个方法中,用我的自定义注释@CatchMe
进行注释,我调用第一个方法。我希望发生的是被记录的异常。不幸的是,抛出了异常,但未触发方面。那么,如何使用 Mockito 在此测试中触发方面呢?
注意:我已经检查了这些(还有更多(:
- 单元测试 Spring @Around AOP 方法
- 弹簧方面在单元测试中未触发
- 弹簧:无法将模拟注入带有@Aspect注释的类
但其中大多数与控制器相关,而不是与服务相关,我只想测试服务。
测试
@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 {}
编辑:该功能有效,但我想通过测试进行验证。
正在按预期工作,但是您正在运行基于代理的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);
}
但是我认为这种方法是错误的,当你开始像这样胡闹时,有更好的方法。只需使用弹簧注入模拟。为此测试用例创建特定的@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();
}
}
这将处理正确的依赖项。
我遇到了与@nyxz相同的问题,这是故意的,请参阅 https://github.com/spring-projects/spring-boot/issues/7243。
受到 Deinum @M启发,以下解决方案在 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;
}
}
}