Java 在Spring测试中未调用方面
我使用的是Spring4.16,我有我的ValidationSpect,它验证方法参数,并在出现错误时抛出ValidationException。当我运行服务器并发送请求时,会调用此函数,但当来自测试时不会调用此函数:Java 在Spring测试中未调用方面,java,spring,spring-aop,spring-test,spring-aspects,Java,Spring,Spring Aop,Spring Test,Spring Aspects,我使用的是Spring4.16,我有我的ValidationSpect,它验证方法参数,并在出现错误时抛出ValidationException。当我运行服务器并发送请求时,会调用此函数,但当来自测试时不会调用此函数: package com.example.movies.domain.aspect; ... @Aspect public class ValidationAspect { private final Validator validator; public Va
package com.example.movies.domain.aspect;
...
@Aspect
public class ValidationAspect {
private final Validator validator;
public ValidationAspect(final Validator validator) {
this.validator = validator;
}
@Pointcut("execution(* com.example.movies.domain.feature..*.*(..))")
private void selectAllFeatureMethods() {
}
@Pointcut("bean(*Service)")
private void selectAllServiceBeanMethods() {
}
@Before("selectAllFeatureMethods() && selectAllServiceBeanMethods()")
public synchronized void validate(JoinPoint joinPoint) {
// Validates method arguments which are annotated with @Valid
}
}
我在其中创建方面bean的配置文件
package com.example.movies.domain.config;
...
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AspectsConfiguration {
@Bean
@Description("Hibernate validator. Used to validate request's input")
public Validator validator() {
ValidatorFactory validationFactory = Validation.buildDefaultValidatorFactory();
return validationFactory.getValidator();
}
@Bean
@Description("Method validation aspect")
public ValidationAspect validationAspect() {
return new ValidationAspect(this.validator());
}
}
这就是测试,它应该在进入addSoftware方法之前抛出ValidationException,因为它是一个无效的softwareObject
@ContextConfiguration
@ComponentScan(basePackages = {"com.example.movies.domain"})
public class SoftwareServiceTests {
private static final Logger LOGGER = LoggerFactory.getLogger(SoftwareServiceTests.class.getName());
private SoftwareService softwareService;
@Mock
private SoftwareDAO dao;
@Mock
private MapperFacade mapper;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
this.softwareService = new SoftwareServiceImpl(this.dao);
((SoftwareServiceImpl) this.softwareService).setMapper(this.mapper);
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SoftwareServiceTests.class);
ctx.getBeanFactory().registerSingleton("mockedSoftwareService", this.softwareService);
this.softwareService = (SoftwareService) ctx.getBean("mockedSoftwareService");
}
@Test(expected = ValidationException.class)
public void testAddInvalidSoftware() throws ValidationException {
LOGGER.info("Testing add invalid software");
SoftwareObject softwareObject = new SoftwareObject();
softwareObject.setName(null);
softwareObject.setType(null);
this.softwareService.addSoftware(softwareObject); // Is getting inside the method without beeing validated so doesn't throws ValidationException and test fails
}
}
如果我运行该服务,并从post请求中添加该无效用户,则会按原样引发ValidationException。但由于某些原因,它从未从测试层执行ValidationSpect方法
还有我的服务
package com.example.movies.domain.feature.software.service;
...
@Service("softwareService")
public class SoftwareServiceImpl
implements SoftwareService {
@Override
public SoftwareObject addSoftware(@Valid SoftwareObject software) {
// If gets into this method then software has to be valid (has been validated by ValidationAspect since is annotated with @Valid)
// ...
}
}
我不明白为什么不调用aspect,因为MockedSoftwareServicebean位于FeaturePackage中,并且bean名称以“Service”结尾,所以它满足这两个条件。你知道会发生什么吗?提前谢谢
编辑
不确定您想做什么,但是您的
@ContextConfiguration
没有用,因为您没有使用Spring测试来运行测试(这需要@RunWith
或Spring测试中的一个超类)
接下来,您将添加一个已经完全模拟和配置的单例(这是上下文假定的)。我强烈建议使用Spring,而不是围绕它工作
首先在您的测试类中创建一个用于测试的配置,这个配置应该扫描并注册模拟bean。第二,使用Spring测试运行您的测试
@ContextConfiguration
public class SoftwareServiceTests extends AbstractJUnit4SpringContextTests {
private static final Logger LOGGER = LoggerFactory.getLogger(SoftwareServiceTests.class.getName());
@Autowired
private SoftwareService softwareService;
@Test(expected = ValidationException.class)
public void testAddInvalidSoftware() throws ValidationException {
LOGGER.info("Testing add invalid software");
SoftwareObject softwareObject = new SoftwareObject();
softwareObject.setName(null);
softwareObject.setType(null);
this.softwareService.addSoftware(softwareObject);
}
@Configuration
@Import(AspectsConfiguration.class)
public static class TestConfiguration {
@Bean
public SoftwareDAO softwareDao() {
return Mockito.mock(SoftwareDAO.class);
}
@Bean
public MapperFacade domainMapper() {
return Mockito.mock(MapperFacade.class)
}
@Bean
public SoftwareService softwareService() {
SoftwareServiceImpl service = new SoftwareServiceImpl(softwareDao())
return service;
}
}
}
了解SpringAOP是如何工作的很好。如果Spring管理的bean符合任何方面(每个方面一个代理)的条件,那么它将被包装在一个代理(或几个代理)中 通常,Spring使用接口创建代理,尽管它可以使用库(如cglib)处理常规类。对于您的服务,这意味着Spring创建的实现实例被包装在一个代理中,该代理处理方法验证的方面调用 现在,您的测试手动创建SoftwareServiceImpl实例,因此它不是Spring管理的bean,因此Spring没有机会将其封装在代理中,以便能够使用您创建的特性
您应该使用Spring来管理bean,以使方面工作。确实有两件重要的事情需要实现: 1) 对象树的根必须由应用程序上下文中注册的扫描对象解析。如果您使用new(),就不可能解析AOP注释 2) 注释和AOP方面类需要注册 ad 1)@Autowire您的根对象将完成此任务 ad 2)确保@Component使用了正确的过滤器: @Component()或@Component(“您的完整命名空间包筛选器”) 检查:
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx)
{
return args ->
{
log.debug("Let's inspect the beans provided by Spring Boot:");
List<String> beanNames = Arrays.asList(ctx.getBeanDefinitionNames());
Assert.isTrue( beanNames.contains("yourAspectClassName"));
};
}
@Bean
公共命令行运行程序命令行运行程序(ApplicationContext ctx)
{
返回参数->
{
debug(“让我们检查SpringBoot提供的bean:”);
List beanNames=Arrays.asList(ctx.getBeanDefinitionNames());
isTrue(beanNames.contains(“yourAspectClassName”);
};
}
方面类是否定义在测试配置xml中?请提供您的测试配置xml。(由@Rodrigo Gomes请求)尝试添加@RunWith(SpringJUnit4ClassRunner.class)
,因为Junit尚未附加到spring上下文:)@Barett我没有任何xml配置。它全部由java注释配置。方面配置正是我在那里提出的。谢谢你的回答@Bond JavaBond我将该RunWith行放入,得到“非法状态异常:GenericXmlContextLoader和AnnotationConfigContextLoader都无法检测默认值,并且没有为上下文配置[ContextConfigurationAttributes]声明ApplicationContextInitializers…”如果在测试上下文中定义bean并将其注入测试用例,会发生什么?我认为问题是一个非托管bean。谢谢您的回复。我没有得到这样的bean定义异常:没有[ma.glasnost.orika.MapperFacade]类型的合格bean。你知道会发生什么吗?那么你还没有包括正确的bean或它们的模拟。你有正确的映射类吗?你没有(出于某种原因)在某个地方进行组件扫描吗?我还想知道为什么你有自己的一面?Spring已经有了MethodValidationInterceptor
(尽管这只适用于hibernate验证器)。您的理解是错误的MethodValidationInterceptor
只适用于控制器之外的所有东西。控制器有本机支持,其他组件没有。请将mapper
重命名为domainMapper
。你还有@Autowired
为什么还要手动注射?我已经解决了这个问题!问题与你刚才说的有关。谢谢在花了24小时之后,这真是一个非常好的建议:)
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx)
{
return args ->
{
log.debug("Let's inspect the beans provided by Spring Boot:");
List<String> beanNames = Arrays.asList(ctx.getBeanDefinitionNames());
Assert.isTrue( beanNames.contains("yourAspectClassName"));
};
}