Could someone please help me out in writing Junit for this piece of code and provide resources to learn the same. I have been trying to figure out from multiple resources but couldn't find anything. I need to mock the pointcuts and methods which are invoked within the pointcut. Is unit testing possible for this using Mockito
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.sample.api.rest.account.AccountResource;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.CustomLog;
import lombok.extern.slf4j.slf4j;
@Aspect
@CustomLog
public class sample {
ObjectMapper mapper = new ObjectMapper();
long startTimeController = 0L;
long endTimeController = 0L;
@Pointcut("within(com.sample.api.rest.account. .) || "
+ "within(com.sample.api.rest.metadata..') ")
public void entryController() {}
@Pointcut("within(com. sample.api.rest.user..*)")
public void entryControllerUser() {}
@Pointcut("within(com.sample.api.service. .*)")
public void entryService() {}
@Pointcut("within(com. sample.cmo.repositories..*)")
public void entryDAO() {}
@Before("entryController()")
public void beforeOtherControllerCall(JoinPoint jp) throws JsonProcessingException {
String methodName = jp.getSignature().getName();
String className = jp.getTarget().getClass().toString();
Object[] arguments = jp.getArgs();
log.info(className + " Method : " + methodName + " Arguments passed : " +
mapper.writeValueAsString(arguments));
startTimeController = System.currentTimeMillis();
}
@Before("entryControllerUser()")
public void beforeUserControllerCall(JoinPoint jp) throws JsonProcessingException {
String methodName = jp.getSignature().getName();
String className = jp.getTarget().getClass().toString();
log.info(className + " Method : " + methodName);
startTimeController = System.currentTimeMillis();
}
@After("entryController() || entryControlleruser()")
public void afterControllerCall(JoinPoint jp) throws JsonProcessingException {
endTimeController = System.currentTimeMillis();
String methodName = jp.getSignature().getName();
String className = jp.getTarget().getClass().toString();
log.info(className + " Method : " + methodName + " Values returned :");
if (endTimeController != 0) {
log.info("Time consumed in " + className + " " + methodName + " call is "
+ (endTimeController - startTimeController) + "ms");
}
}
@Around("entryService()")
public Object executionTimeService(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
String className = pjp.getTarget().getClass().toString();
Object[] arguments = pjp.getArgs();
log.info(className + " Method: " + methodName + " Arguments passed :" +
mapper.writeValueAsString(arguments));
long startTime = System.currentTimeMillis();
Object obj = pip.proceed();
long endTime = System.currentTimeMillis();
log.info(className + " Method : " + methodName + " Execution time: " + (endTime -
startTime) + "ms");
log.info(className + " Method : " + methodName + " Response received : " +
mapper.writeValueAsString(obj));
return obj;
}
@Around("entryDAO()")
public Object executionTimeDAO(ProceedingJoinPoint pjp ) throws Throwable {
String methodName pjp.getSignature().getName();
String className pjp.getTarget().getClass().toString();
Object[] arguments = pjp.getArgs();
log.info(className+" Method : "+methodName+" Arguments passed :"
+mapper.writeValueAsString(arguments) );
long startTime = System.currentTimeMillis();
Object obj = pip.proceed();
long endTime = System.currentTimeMillis();
log.info(className+" method : " + methodName+" Execution time: "
+(endTime-start Time)+"ms" );
log.info(className+" Method: "+methodName+" Response received : "+
mapper.writeValueAsString(obj));
return obj;
}
}
Here is the sample of what I have tried with
@Test
public void testBeforeOtherControllerCall() throws Throwable{
JoinPoint joinPoint = mock(JoinPoint.class);
AspectLogging logging = mock(AspectLogging.class);
String[] args = {"arg1", "arg2"};
Object[] obj args)
Signature signature = mock (Signature.class);
when(joinPoint.getSignature().thenReturn(signature);
when(signature.getName().thenReturn("MethodName");
Object object = mock(Object.class);
when(joinPoint.getTarget().thenReturn(object);
when(object.getClass().thenReturn(objectClass);
when(joinPoint.getArgs().thenReturn(obj);
logging.beforeOtherControllerCali(joinPoint);
verify(joinPoint, times (1)).getSignature().getName().equals("MethodName");
}
When trying to recreate your situation, I had to
guess about which libraries and versions you use,
replace the Lombok logging annotation by a regularly created SLF4J logger, because, as the AspectJ compiler tells you in a warning message, Lombok's byte code modifications do not work with the AspectJ compiler: java: You aren't using a compiler supported by lombok, so lombok will not work and has been disabled. (...) Lombok supports: sun/apple javac 1.6, ECJ
. When using Spring AOP instead, it probably works with Lombok, because there you are simply using the normal Java compiler. I however tested in native AspectJ.
fix many syntax errors in your aspect and test Java code as well as several syntax errors in your aspect pointcuts. The classes did not even compile and the aspect cannot have done anything meaningful with faulty pointcuts. If you modify original code in order to create a stand-alone example, please test before posting it. You should have a little bit more pride as a developer when presenting your work publicly. This was your free shot, because you are new on SO, but next time I will just close the question.
Please do read the MCVE article, try to understand it and ask better questions in the future.
package de.scrum_master.aspect;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Aspect
public class LoggingAspect {
private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);
ObjectMapper mapper = new ObjectMapper();
long startTimeController = 0L;
long endTimeController = 0L;
@Pointcut("within(com.sample.api.rest.account..*) || within(com.sample.api.rest.metadata..*)")
public void entryController() {}
@Pointcut("within(com.sample.api.rest.user..*)")
public void entryControllerUser() {}
@Pointcut("within(com.sample.api.service..*)")
public void entryService() {}
@Pointcut("within(com.sample.cmo.repositories..*)")
public void entryDAO() {}
@Before("entryController()")
public void beforeOtherControllerCall(JoinPoint jp) throws JsonProcessingException {
String methodName = jp.getSignature().getName();
String className = jp.getTarget().getClass().toString();
Object[] arguments = jp.getArgs();
log.info(className + " Method : " + methodName + " Arguments passed : " + mapper.writeValueAsString(arguments));
startTimeController = System.currentTimeMillis();
}
@Before("entryControllerUser()")
public void beforeUserControllerCall(JoinPoint jp) throws JsonProcessingException {
String methodName = jp.getSignature().getName();
String className = jp.getTarget().getClass().toString();
log.info(className + " Method : " + methodName);
startTimeController = System.currentTimeMillis();
}
@After("entryController() || entryControllerUser()")
public void afterControllerCall(JoinPoint jp) throws JsonProcessingException {
endTimeController = System.currentTimeMillis();
String methodName = jp.getSignature().getName();
String className = jp.getTarget().getClass().toString();
log.info(className + " Method : " + methodName + " Values returned :");
if (endTimeController != 0) {
log.info("Time consumed in " + className + " " + methodName + " call is " + (endTimeController - startTimeController) + "ms");
}
}
@Around("entryService()")
public Object executionTimeService(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
String className = pjp.getTarget().getClass().toString();
Object[] arguments = pjp.getArgs();
log.info(className + " Method: " + methodName + " Arguments passed :" + mapper.writeValueAsString(arguments));
long startTime = System.currentTimeMillis();
Object obj = pjp.proceed();
long endTime = System.currentTimeMillis();
log.info(className + " Method : " + methodName + " Execution time: " + (endTime - startTime) + "ms");
log.info(className + " Method : " + methodName + " Response received : " + mapper.writeValueAsString(obj));
return obj;
}
@Around("entryDAO()")
public Object executionTimeDAO(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
String className = pjp.getTarget().getClass().toString();
Object[] arguments = pjp.getArgs();
log.info(className + " Method : " + methodName + " Arguments passed :" + mapper.writeValueAsString(arguments));
long startTime = System.currentTimeMillis();
Object obj = pjp.proceed();
long endTime = System.currentTimeMillis();
log.info(className + " method : " + methodName + " Execution time: " + (endTime - startTime) + "ms");
log.info(className + " Method: " + methodName + " Response received : " + mapper.writeValueAsString(obj));
return obj;
}
}
I sent you two links before in my comment, showing you how to write unit and integration tests. Why did you not do it more similar to my examples? Some things you did wrong are:
You created a mock for the aspect class under test. Why? You want to mock dependencies, not the thing you actually want to test. Like in my examples, you should instantiate the aspect normally, only inject a mock joinpoint when calling an advice method.
You cannot simply verify a chain of method calls on a corresponding chain of mock objects, but need to verify them separately. So, something like verify(joinPoint, times (1)).getSignature().getName().equals("MethodName")
does not work.
You tried to stub when(object.getClass()).thenReturn(objectClass)
, which is unnecessary, because Object.getClass()
returns something already. Furthermore, it is a final method of a JDK bootstrap class. You cannot simply mock that.
How about this?
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class LoggingAspectTest {
@Test
public void testBeforeOtherControllerCall() throws Throwable {
JoinPoint joinPoint = mock(JoinPoint.class);
LoggingAspect logging = new LoggingAspect();
String[] args = { "arg1", "arg2" };
Object[] obj = args;
Signature signature = mock(Signature.class);
when(joinPoint.getSignature()).thenReturn(signature);
when(signature.getName()).thenReturn("MethodName");
Object object = mock(Object.class);
when(joinPoint.getTarget()).thenReturn(object);
when(joinPoint.getArgs()).thenReturn(obj);
logging.beforeOtherControllerCall(joinPoint);
verify(joinPoint, times(1)).getSignature();
verify(signature, times(1)).getName();
verify(joinPoint, times(1)).getTarget();
verify(joinPoint, times(1)).getArgs();
}
}
This covers the method under test and verifies the calls on the mock objects you are interested in, even though I find those verifications somewhat questionable. Do you really want to test the internals of the aspect? You should rather test for side effects or results, if any. But of course you can do it like in my example.
My IDE looks like this when running the test with coverage: