0

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");
}
kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Welcome to SO. Please first learn how to ask questions, using an [MCVE](https://stackoverflow.com/help/mcve). Thank you very much. The way SO works is: You present your problem and the solution you tried, describing what result you expected and what the actual result was. Then someone helps you fix your code. SO is not: "Please do my job for me, solving my problem on my behalf." You cannot expect anyone to write the whole unit test for you. So please improve the question, then I am sure someone is going to help you. – kriegaex Jul 01 '21 at 07:04
  • Meanwhile, my answers about [AspectJ unit tests](https://stackoverflow.com/a/41407336/1082681) and [AspectJ integration tests](https://stackoverflow.com/a/41453156/1082681) should help you get started. Both involve use of Mockito. – kriegaex Jul 01 '21 at 07:06
  • @kriegaex Thank you so much for letting me know. I would also post the code that I have tried and then would modify the question accordingly. – Sathish Ramakrishnan Jul 02 '21 at 02:53
  • @kriegaex I have tried and posted based on the resources that you gave me. Could you guide me further? – Sathish Ramakrishnan Jul 05 '21 at 12:55
  • I can take a look tomorrow. Would you meanwhile please add a description of the current status? Is the test running successfully? Are there any errors? Stack traces? Or simply not enough coverage? You forgot to mention what is wrong and where you need guidance. – kriegaex Jul 05 '21 at 14:05
  • @kriegaex I understand your point. From now on I will go through the MVCE for questions before posting them and would revalidate my questions prior to asking them. Thank you for the help. – Sathish Ramakrishnan Jul 06 '21 at 06:11

1 Answers1

0

Preface

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.

Fixed aspect code

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;
  }
}

Fixed test

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:

Test coverage

kriegaex
  • 63,017
  • 15
  • 111
  • 202