I am using Spring 4.16 and i have my ValidationAspect, which validates methods arguments and throws ValidationException if is something wrong. This is being called when i run the server and send requests, but not when comes from the test:
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
}
}
The config file where i create aspect the aspect 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());
}
}
So this is the test, it should throw ValidationException just before it gets into addSoftware method, since is an invalid 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
}
}
If i run the service and i add this invalid user from a post request, this throws ValidationException as it should be. But for some reason, it is never executing ValidationAspect method from the test layer
And my service
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)
// ...
}
}
I dont understand why aspect is not being called, since mockedSoftwareService bean is located in feature package and the bean name ends with "Service", so it satisfies both conditions. Do you have any idea about what could be happening ? Thanks in advance
@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
}
Not sure what you are trying to do but your @ContextConfiguration
is useless as you aren't using Spring Test to run your test (that would require a @RunWith
or one of the super classes from Spring Test).
Next you are adding a singleton which is already fully mocked and configured (that is what the context assumes). I strongly suggest to use Spring instead of working around it.
First create a configuration inside your test class for testing, this configuration should do the scanning and register the mocked bean. Second use Spring Test to run your 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;
}
}
}