Spring AOP: aspect is not executing in case of MethodConstraintViolationException - java

I have a bean annoteded with JSR 303 annotations. I also added Spring aspect (#Around) for handling MethodConstraintViolationException. My problem is: if I execute methods with correct parameters - my aspect works (is executed - breakpoints added), but when I run methods with incorrect parameters then MethodConstraintViolationException is thrown and my aspect is not executed.
package noname.exception;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.hibernate.validator.method.MethodConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import noname.service.exceptions.ValidationException;
import noname.utils.ValidationExceptionProcessor;
#Aspect
public class ExceptionAspect implements Ordered {
#Autowired
private ValidationExceptionProcessor processor;
#Pointcut(value = "execution(* noname.conversionstrategy.api.IDocumentConverter.*(..))")
public void aopDocumentConverterPointcut() {
}
#Pointcut(value = "execution(* noname.service.api.IMailMerger.*(..))")
public void aopMailMargeServicePointcut() {
}
#SuppressWarnings("deprecation")
#Around("aopDocumentConverterPointcut() || aopMailMargeServicePointcut()")
public Object exceptionsAspect(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
try {
return proceedingJoinPoint.proceed();
} catch ( Throwable e ) {
if (e instanceof MethodConstraintViolationException) {
ValidationException exp = processor.process((MethodConstraintViolationException) e);
throw exp;
} else {
throw e;
}
}
}
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
Intefaces (IMailMerger and IDocumentConverter) are similar:
package noname.conversionstrategy.api;
import java.util.List;
import javax.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;
import noname.service.domain.DocumentActionInput;
import noname.service.domain.DocumentActionResult;
import noname.validator.ValidActionInput;
#Validated
public interface IDocumentConverter {
DocumentActionResult convertDocument(#NotNull(message = "DocumentActionInput must be provided") #ValidActionInput DocumentActionInput document);
List<DocumentActionResult> convertDocuments(#NotNull(message = "DocumentActionInput must be provided") #ValidActionInput List<DocumentActionInput> documents);
}
I suppose spring execute first bean validation (it is probably executed with aspect too (?) ). If this validation throws MethodConstraintViolationException then my aspect is not executed, because spring aop doesn't support catching exceptions from anoother aspect (need confirmation).
I also created test with proxy (everything with my aspect looks fine):
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:applicationContextTest.xml"})
public class ExceptionAspectSpringTest {
#Autowired
private IDocumentConverter documentConverter;
#Autowired
private ExceptionAspect exceptionAspect;
private IDocumentConverter proxy;
#Before
public void setUp() {
AspectJProxyFactory aspectJProxyFactory = new AspectJProxyFactory(documentConverter);
aspectJProxyFactory.addInterface(IDocumentConverter.class);
aspectJProxyFactory.addAspect(exceptionAspect);
proxy = aspectJProxyFactory.getProxy();
}
#Test( expected = ValidationException.class )
public void shouldThownValidationException() {
DocumentActionInput document = new DocumentActionInput();
proxy.convertDocument(document);
}
}
Any help appreciated

Related

Test fails on a null pointer exception when I use #InjectMocks

I am practising restful api endpoints using https://api.predic8.de/shop/docs
Here is my repo
I am getting a NPE failure when I try to use #InjectMocks during my TDD approach
However, I can make my test pass when I make a direct call in the setup()
vendorService = new VendorServiceImpl(VendorMapper.INSTANCE, vendorRepository);
I wanted to extend my learning by trying to create an endpoint for getting all vendors.
When I employ TDD along the way, but, my test getAllVendors() fails on a NPE when I try to use #InjectMocks but passes when I substitute it for a direct call in the setup() method.
The NPE is linked to the mapper class I think.
Here are the classes that I believe are useful. VendorServiceTest, VendorServiceImpl, VendorMapper.
I have commented out the direct call in the setup as I want to get the test passing using #InjectMocks
package guru.springfamework.services;
import guru.springfamework.api.v1.mapper.VendorMapper; import
guru.springfamework.api.v1.model.VendorDTO; import
guru.springfamework.domain.Vendor; import
guru.springfamework.repositories.VendorRepository; import
org.junit.Before; import org.junit.Test; import
org.mockito.InjectMocks; import org.mockito.Mock; import
org.mockito.MockitoAnnotations; import
org.springframework.test.web.servlet.MockMvc;
import java.util.Arrays; import java.util.List;
import static org.junit.Assert.*; import static
org.mockito.Mockito.when;
public class VendorServiceTest {
public static final String NAME = "Tasty";
public static final Long ID = 1L;
#Mock
VendorMapper vendorMapper;
#Mock
VendorRepository vendorRepository;
#InjectMocks
VendorServiceImpl vendorService;
//VendorService vendorService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
//vendorService = new VendorServiceImpl(VendorMapper.INSTANCE, vendorRepository);
}
#Test
public void getAllVendors() {
//given
List<Vendor> vendors = Arrays.asList(new Vendor(), new Vendor(), new Vendor());
when(vendorRepository.findAll()).thenReturn(vendors);
//when
List<VendorDTO> vendorDTOList = vendorService.getAllVendors();
//then
assertEquals(3, vendorDTOList.size());
}
#Test
public void findByName() {
}
}
package guru.springfamework.services;
import guru.springfamework.api.v1.mapper.VendorMapper; import
guru.springfamework.api.v1.model.VendorDTO; import
guru.springfamework.repositories.VendorRepository; import
org.springframework.stereotype.Service;
import java.util.List; import java.util.stream.Collectors;
#Service public class VendorServiceImpl implements VendorService {
private final VendorMapper vendorMapper;
private final VendorRepository vendorRepository;
public VendorServiceImpl(VendorMapper vendorMapper, VendorRepository vendorRepository) {
this.vendorMapper = vendorMapper;
this.vendorRepository = vendorRepository;
}
#Override
public List<VendorDTO> getAllVendors() {
return vendorRepository
.findAll()
.stream()
.map(vendor -> {
VendorDTO vendorDTO = vendorMapper.vendorToVendorDTO(vendor);
vendorDTO.setVendorUrl("/api/v1/vendors/" + vendor.getId());
return vendorDTO;
})
.collect(Collectors.toList());
}
#Override
public VendorDTO findByName(String name) {
return vendorMapper.vendorToVendorDTO(vendorRepository.findByName(name));
}
#Override
public VendorDTO getVendorById(Long id) {
return vendorMapper.vendorToVendorDTO(vendorRepository.findById(id).orElseThrow(RuntimeException::new));
}
}
package guru.springfamework.api.v1.mapper;
import guru.springfamework.api.v1.model.VendorDTO; import
guru.springfamework.domain.Vendor; import org.mapstruct.Mapper; import
org.mapstruct.factory.Mappers;
#Mapper public interface VendorMapper {
VendorMapper INSTANCE = Mappers.getMapper(VendorMapper.class);
VendorDTO vendorToVendorDTO(Vendor vendor);
}
Does anyone know where and why I am going wrong?
The problem is that you created mock object for the mapper, but you didn't say what should happen when the method vendorToVendorDTO is called.
Therefore, when that method is called in the next line of code:
VendorDTO vendorDTO = vendorMapper.vendorToVendorDTO(vendor);
It will return null, and then in this line of code:
vendorDTO.setVendorUrl("/api/v1/vendors/" + vendor.getId());
You will get NullPointerException.
To make this work, change your getAllVendors() method as follows:
#Test
public void getAllVendors() {
//given
List<Vendor> vendors = Arrays.asList(new Vendor(), new Vendor(), new Vendor());
VendorDTO mockDto = mock(VendorDTO.class);
when(vendorRepository.findAll()).thenReturn(vendors);
when(vendorMapper.vendorToVendorDTO(any(Vendor.class))).thenReturn(mockDto);
//when
List<VendorDTO> vendorDTOList = vendorService.getAllVendors();
//then
assertEquals(3, vendorDTOList.size());
}
And the test should pass.
Have you tried to put #RunWith(MockitoJUnitRunner.class)/#ExtendsWith(MockitoExtension.class) over your test class?

How to mock ServiceLoader class in Java

public class ServiceLoaderDemo {
public CPService demo() {
ServiceLoader<CPService> serviceLoader = ServiceLoader.load(CPService.class);
return iterate(serviceLoader);
}
public CPService iterate(ServiceLoader<CPService> serviceLoader) {
CPService nonDefault = null;
for (CPService cpService : serviceLoader) {
cpService.show();
if(!cpService.isDefault())
{
nonDefault = cpService;
}
}
return nonDefault;
}
}
I need to write unit tests for iterate method with following cases:
a default service and a non-default service
a default service only
a non-default service only
more than one non-default service
I tried to mock ServiceLoader class as follows:
import static org.fest.reflect.core.Reflection.method;
import static org.powermock.api.mockito.PowerMockito.mock;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import junit.framework.TestCase;
#RunWith(PowerMockRunner.class)
#PrepareForTest(ServiceLoader.class)
public class ServiceLoaderDemoTest extends TestCase {
private ServiceLoaderDemo serviceLoaderDemo = new ServiceLoaderDemo();
public void testIterate() {
final ServiceLoader mockServiceLoader = mock(ServiceLoader.class);
IteratorDummy iterator = new IteratorDummy();
iterator.cpServices.add(new CPServiceImplTwo());
iterator.cpServices.add(new CPServiceImplOne());
PowerMockito.when(mockServiceLoader.iterator()).thenReturn(iterator);
CPService methodToTest = null;
try {
methodToTest = method("iterate").withReturnType(CPService.class).withParameterTypes(ServiceLoader.class).in(serviceLoaderDemo).invoke(mockServiceLoader);
} catch (SecurityException e) {
e.printStackTrace();
}
assertEquals(methodToTest.getClass(), ServiceLoaderDemoTest.class);
}
}
class IteratorDummy implements Iterator<CPService> {
public List<CPService> cpServices = new ArrayList<>();
#Override
public boolean hasNext() {
return cpServices.iterator().hasNext();
}
#Override
public CPService next() {
CPService service = cpServices.iterator().next();
return service;
}
}
This is throwing NullPointerException. I am unable to write unit tests for this. please help me.

pointcut for method in parent abstract class

I have a scenario where my method to be intercepted is in the parent class and is not overridden in the pointcut class.
Here is the sample classes:
public abstract class A{
#RequestMapping(value = "/data", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String getData(#RequestBody String request) throws Exception {
return "dummy";
}
}
#RestController
public class B extends A {
}
My Aspect is defined as:
#Aspect
#Component
public class RestCallLogger {
#Pointcut("within(com.test..*) && within(#org.springframework.web.bind.annotation.RestController *)")
public void restControllers() {
}
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMappingAnnotations() {
}
#Around("restControllers() && requestMappingAnnotations()")
public Object onExecute(ProceedingJoinPoint jp) throws Throwable {
Object result = null;
try {
result = jp.proceed();
} catch (Exception ex) {
throw ex;
}
return result;
}
}
But its not working. If I mark class A with Annotation #RestController and make it concrete, then it works.
The question is how can I create a "pointcut for method in parent abstract class"?
PS: I can not change the hierarchy of the code as its the existing code.
For me this works. Here is an MCVE:
package de.scrum_master.app;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
public abstract class A {
#RequestMapping(value = "/data", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String getData(#RequestBody String request) throws Exception {
return request;
}
}
package de.scrum_master.app;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class B extends A {}
package de.scrum_master.app;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass = true)
#ComponentScan(basePackages = { "de.scrum_master" })
public class Application2 {
public static void main(String[] args) throws Exception {
ApplicationContext appContext = new AnnotationConfigApplicationContext(Application2.class);
B b = (B) appContext.getBean("b");
System.out.println(b.getData("bbb"));
A a = (A) appContext.getBean("b");
System.out.println(a.getData("aaa"));
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class RestCallLogger {
#Pointcut("within(de.scrum_master..*) && #target(org.springframework.web.bind.annotation.RestController)")
public void restControllers() {}
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMappingAnnotations() {
}
#Around("restControllers() && requestMappingAnnotations()")
public Object onExecute(ProceedingJoinPoint jp) throws Throwable {
System.out.println(jp);
Object result = null;
try {
result = jp.proceed();
} catch (Exception ex) {
throw ex;
}
return result;
}
}
The console log says:
execution(String de.scrum_master.app.A.getData(String))
bbb
execution(String de.scrum_master.app.A.getData(String))
aaa
What is different in your case?

Spring AOP Logging and Cache

I'm logging method input and output parameters by a simple Aspect.
package com.mk.cache;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
#Aspect
#Component
public class LoggingAspect {
#Around("within(#com.mk.cache.LoggedIO *) && execution(* *(..))")
public Object logAroundPublicMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String wrappedClassName = joinPoint.getSignature().getDeclaringTypeName();
Logger LOGGER = LoggerFactory.getLogger(wrappedClassName);
String methodName = joinPoint.getSignature().getName();
LOGGER.info("LOG by AOP - invoking {}({})", methodName, Arrays.toString(joinPoint.getArgs()));
Object result = joinPoint.proceed();
LOGGER.info("LOG by AOP - result of {}={}", methodName, result);
return result;
}
}
which is attached by this Annotation.
package com.mk.cache;
public #interface LoggedIO {
}
I use this mechanism to log inputs and outputs of methods like this (notice #LoggedIO):
package com.mk.cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
#Service
#LoggedIO
public class CachedService {
private static final Logger LOGGER = LoggerFactory.getLogger(CachedService.class);
#Cacheable("myCacheGet")
public int getInt(int input) {
LOGGER.info("Doing real work");
return input;
}
}
I also use Spring Cache. Here is the example application.
package com.mk.cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class CacheApplication implements CommandLineRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(CacheApplication.class);
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
#Autowired
private CachedService cachedService;
#Override
public void run(String... args) throws Exception {
LOGGER.info("cachedService.getInt(1)={}", cachedService.getInt(1));
LOGGER.info("cachedService.getInt(1)={}", cachedService.getInt(1));
}
}
The output looks like this:
LOG by AOP - invoking getInt([1])
Doing real work
LOG by AOP - result of getInt=1
cachedService.getInt(1)=1
cachedService.getInt(1)=1
My problem is, that when I call LOGGER.info("cachedService.getInt(1)={}", cachedService.getInt(1)); for the second time, the cached value is used, but the input and output parameters are not logged, as the cache is the first wrapper. Is it possible to somehow configure the LoggingAspect to be the first wrapper, so I will be able to use both AOP logging and both Spring Cache?
Just implement spring Ordered interface and in getOrder() method return 1.
#Aspect
#Component
public class LoggingAspect implements Ordered {
#Around("within(#com.mk.cache.LoggedIO *) && execution(* *(..))")
public Object logAroundPublicMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String wrappedClassName = joinPoint.getSignature().getDeclaringTypeName();
Logger LOGGER = LoggerFactory.getLogger(wrappedClassName);
String methodName = joinPoint.getSignature().getName();
LOGGER.info("LOG by AOP - invoking {}({})", methodName, Arrays.toString(joinPoint.getArgs()));
Object result = joinPoint.proceed();
LOGGER.info("LOG by AOP - result of {}={}", methodName, result);
return result;
}
#Override
public int getOrder() {
return 1;
}
}
Read more here. Chapter 11.2.7

Detect Failure or Error of Junit Test in #After method

Is there a way in JUnit to detect within an #After annotated method if there was a test failure or error in the test case?
One ugly solution would be something like that:
boolean withoutFailure = false;
#Test
void test() {
...
asserts...
withoutFailure = true;
}
#After
public void tearDown() {
if(!withoutFailuere) {
this.dontReuseTestenvironmentForNextTest();
}
}
This is ugly because one need to take care of the "infrastructure" (withoutFailure flag) in the test code.
I hope that there is something where I can get the test status in the #After method!?
If you are lucky enough to be using JUnit 4.9 or later, TestWatcher will do exactly what you want.
Share and Enjoy!
I extend dsaff's answer to solve the problem that a TestRule can not execute some code snipped between the execution of the test-method and the after-method. So with a simple MethodRule one can not use this rule to provide a success flag that is use in the #After annotated methods.
My idea is a hack! Anyway, it is to use a TestRule (extends TestWatcher). A TestRule will get knowledge about failed or success of a test. My TestRule will then scan the class for all Methods annotated with my new AfterHack annotations and invoke that methods with a success flag.
AfterHack annotation
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Retention(RUNTIME)
#Target(METHOD)
public #interface AfterHack {}
AfterHackRule
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
public class AfterHackRule extends TestWatcher {
private Object testClassInstance;
public AfterHackRule(final Object testClassInstance) {
this.testClassInstance = testClassInstance;
}
protected void succeeded(Description description) {
invokeAfterHackMethods(true);
}
protected void failed(Throwable e, Description description) {
invokeAfterHackMethods(false);
}
public void invokeAfterHackMethods(boolean successFlag) {
for (Method afterHackMethod :
this.getAfterHackMethods(this.testClassInstance.getClass())) {
try {
afterHackMethod.invoke(this.testClassInstance, successFlag);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
throw new RuntimeException("error while invoking afterHackMethod "
+ afterHackMethod);
}
}
}
private List<Method> getAfterHackMethods(Class<?> testClass) {
List<Method> results = new ArrayList<>();
for (Method method : testClass.getMethods()) {
if (method.isAnnotationPresent(AfterHack.class)) {
results.add(method);
}
}
return results;
}
}
Usage:
public class DemoTest {
#Rule
public AfterHackRule afterHackRule = new AfterHackRule(this);
#AfterHack
public void after(boolean success) {
System.out.println("afterHack:" + success);
}
#Test
public void demofails() {
Assert.fail();
}
#Test
public void demoSucceeds() {}
}
BTW:
1) Hopefully there is a better solution in Junit5
2) The better way is to use the TestWatcher Rule instead of the #Before and #After Method at all (that is the way I read dsaff's answer)
#see
I don't know any easy or elegant way to detect the failure of a Junit test in an #After method.
If it is possible to use a TestRule instead of an #After method, one possibility to do it is using two chained TestRules, using a TestWatcher as the inner rule.
Example:
package org.example;
import static org.junit.Assert.fail;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
public class ExampleTest {
private String name = "";
private boolean failed;
#Rule
public TestRule afterWithFailedInformation = RuleChain
.outerRule(new ExternalResource(){
#Override
protected void after() {
System.out.println("Test "+name+" "+(failed?"failed":"finished")+".");
}
})
.around(new TestWatcher(){
#Override
protected void finished(Description description) {
name = description.getDisplayName();
}
#Override
protected void failed(Throwable e, Description description) {
failed = true;
}
})
;
#Test
public void testSomething(){
fail();
}
#Test
public void testSomethingElse(){
}
}

Categories