Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aspect not being called in Spring test

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


EDIT

@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

}
like image 516
jscherman Avatar asked Jun 08 '15 21:06

jscherman


2 Answers

It is good to understand how Spring AOP works. A Spring managed bean gets wrapped in a proxy (or a few) if it is eligible for any aspect (one proxy per aspect).

Typically, Spring uses the interface to create proxies though it can do with regular classes using libraries like cglib. In case of your service that means the implementation instance Spring creates is wrapped in a proxy that handles aspect call for the method validation.

Now your test creates the SoftwareServiceImpl instance manually so it is not a Spring managed bean and hence Spring has no chance to wrap it in a proxy to be able to use the aspect you created.

You should use Spring to manage the bean to make the aspect work.

like image 170
Ondrej Burkert Avatar answered Oct 10 '22 12:10

Ondrej Burkert


you need to run with srping:

@EnableAspectJAutoProxy
@RunWith(SpringJUnit4ClassRunner.class)
public class MyControllerTest {

}
like image 21
peter zhang Avatar answered Oct 10 '22 13:10

peter zhang