jsc*_*man 8 java spring spring-test spring-aop spring-aspects
我正在使用Spring 4.16并且我有我的ValidationAspect,它验证方法参数并抛出ValidationException如果有问题.这是在我运行服务器并发送请求时调用,但不是在测试时调用:
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
}
}
Run Code Online (Sandbox Code Playgroud)
配置文件,我创建方面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());
}
}
Run Code Online (Sandbox Code Playgroud)
所以这是测试,它应该在它进入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
}
}
Run Code Online (Sandbox Code Playgroud)
如果我运行该服务并从post请求中添加此无效用户,则会抛出ValidationException.但由于某种原因,它永远不会从测试层执行ValidationAspect方法
而我的服务
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)
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
我不明白为什么没有调用方面,因为mockedSoftwareService bean位于功能包中,bean名称以"Service"结尾,因此它满足两个条件.你对可能发生的事情有任何想法吗?提前致谢
@Service("softwareService")
public class SoftwareServiceImpl
implements SoftwareService {
private static final Logger LOGGER = LoggerFactory.getLogger(SoftwareServiceImpl.class.getName());
private SoftwareDAO dao;
private MapperFacade mapper;
@Autowired
private SoftwareCriteriaSupport criteriaSupport;
@Autowired
private SoftwareDefaultValuesLoader defaultValuesLoader;
@Autowired
public SoftwareServiceImpl(SoftwareDAO dao) {
this.dao = dao;
}
@Autowired
@Qualifier("domainMapper")
public void setMapper(MapperFacade mapper) {
this.mapper = mapper;
}
// other methods
}
Run Code Online (Sandbox Code Playgroud)
不确定您要做什么,但是您@ContextConfiguration
没有用,因为您没有使用Spring Test来运行测试(这需要@RunWith
Spring Test中的一个或一个超级类)。
接下来,您将添加一个已经完全模拟和配置的单例(这是上下文所假定的)。我强烈建议使用Spring而不是解决它。
首先在测试类中创建一个配置以进行测试,此配置应进行扫描并注册模拟的bean。其次使用Spring Test来运行测试。
@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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
了解 Spring AOP 的工作原理很好。如果 Spring 托管 bean 适合任何方面(每个方面一个代理),则它会被包装在一个(或几个)代理中。
通常,Spring 使用该接口来创建代理,尽管它可以使用 cglib 之类的库来处理常规类。如果您的服务意味着 Spring 创建的实现实例被包装在一个代理中,该代理处理方法验证的方面调用。
现在您的测试手动创建 SoftwareServiceImpl 实例,因此它不是 Spring 托管的 bean,因此 Spring 没有机会将它包装在代理中以便能够使用您创建的方面。
您应该使用 Spring 来管理 bean 以使方面工作。