如何为此方面编写 Junit Mockito 代码 class 以获得最大代码覆盖率

How to write the Junit Mockito code for this Aspect class for maximum code coverage

有人可以帮我为这段代码编写 Junit 并提供资源来学习它吗?我一直试图从多种资源中找出答案,但找不到任何东西。我需要模拟切入点和切入点中调用的方法。是否可以使用 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;
    }
}

这是我尝试过的示例

@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");
}

前言

在尝试重现您的情况时,我不得不

  • 猜猜你使用了哪些库和版本,

  • 用定期创建的 SLF4J 记录器替换 Lombok 日志注释,因为正如 AspectJ 编译器在警告消息中告诉您的那样,Lombok 的字节码修改不适用于 AspectJ 编译器: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.当改用 Spring AOP 时,它可能适用于 Lombok,因为你只是在使用普通的 Java 编译器。然而,我在本机 AspectJ 中进行了测试。

  • 修复方面中的许多语法错误并测试 Java 代码以及方面切入点中的几个语法错误。 classes 甚至没有编译,并且方面不能用错误的切入点做任何有意义的事情。如果您修改原始代码以创建独立示例,请在发布之前进行测试。在公开展示您的作品时,作为开发人员您应该多一点自豪感。这是你的免费机会,因为你是 SO 的新手,但下次我会关闭这个问题。

请务必阅读MCVE文章,尝试理解它并在以后提出更好的问题。

固定方面代码

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

固定测试

我之前在评论中给你发了两个链接,向你展示了如何编写单元测试和集成测试。你为什么不把它做得更像我的例子?你做错的一些事情是:

  • 您为正在测试的 class 方面创建了一个模拟。为什么?你想要模拟依赖关系,而不是你真正想要测试的东西。就像在我的示例中一样,您应该正常实例化方面,仅在调用建议方法时注入模拟连接点。

  • 您不能简单地验证相应的模拟对象链上的方法调用链,而是需要单独验证它们。所以,像 verify(joinPoint, times (1)).getSignature().getName().equals("MethodName") 这样的东西是行不通的。

  • 您试图存根 when(object.getClass()).thenReturn(objectClass),这是不必要的,因为 Object.getClass() returns 已经有东西了。此外,它是一个JDKbootstrapclass的最终方法。你不能简单地嘲笑它。

这个怎么样?

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

这涵盖了被测方法并验证了对您感兴趣的模拟对象的调用,尽管我发现这些验证有些问题。您真的要测试方面的内部结构吗?如果有的话,您应该测试副作用或结果。但是你当然可以像我的例子那样做。

我的 IDE 在 运行 覆盖测试时看起来像这样: