代码覆盖率测试时使用jacocoagent.jar的问题

1. 问题描述

项目中遇到的一个问题,简要描述下过程:有两个系统A和B,用户请求A系统的接口a,a接口获取到用户请求参数对象后,再结合A系统中缓存的用户信息构建新的对象参数obj,然后a接口传入参数obj请求B系统的下载接口B。就是这样一个A系统请求B系统接口的事,问题是:开发环境操作时,一切正常,但是测试人员进行测试的时候,显示的却是空白页面,无法下载。通过查阅发现,是测试人员在启动项目的时候,使用jacocoagent.jar进行代码覆盖率的统计。下面使用一个简单的来说明一下吧。

2.Demo演示

2.1 首先创建一个实体对象User

public class User {

    private String username;

    private String sex;

    private int age;

    public User() {
    }

    public User(String username, String sex, int age) {
        this.username = username;
        this.sex = sex;
        this.age = age;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                '}';
    }

2.2 创建一个用户请求接口/test/call

@PostMapping(value = "/call")
public String call(HttpServletResponse response) {
    User user = new User();
    user.setUsername("duan");
    user.setAge(18);
    user.setSex("lady");
    String url = getGetUrl(URL, user, User.class);
    httpUtil.doGetForDownload(response, url);
    return url;
}

因为get请求是将参数通过&符号拼接在url后面的,所以这里专门写了一个方法getGetUrl用来将一个对象拼接到给定的URL后面,这里URL定义如下:

private static final String URL = "http://localhost:8080/user/createUser";

getGetUrl方法如下:

    /**
     * 拼接Get请求的URL
     * @param url
     * @param obj
     * @return
     */
    private static String getGetUrl(String url, Object obj, Class classType) {
        StringBuilder sb = new StringBuilder().append(url).append("?1=1");
        try {
            Field[] fields = classType.getDeclaredFields();
            for(Field field : fields) {
                field.setAccessible(true);
                String name = field.getName();
                Object value = field.get(obj);
                if (value == null) {
                    sb.append("&").append(name).append("=");
                } else if (value instanceof List){
                    for (Object var : ((List) value).toArray()) {
                        sb.append("&").append(name).append("=").append(var);
                    }
                } else {
                    sb.append("&").append(name).append("=").append(value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

在httpUtil.doGetForDownload方法中打印出请求的URL。

现在,我们来启动项目,第一次启动项目时先不添加启动参数,请求call接口没有问题。第二次启动时,添加启动参数:

-javaagent:E:/temp/jacoco-0.8.6/lib/jacocoagent.jar=destfile=E:/temp/jacoco.txt

在进行这一步操作时,需要下载jacocoagent.jar,将其放在指定的目录下,我这里临时放在了E:/temp/jacoco-0.8.6/lib下面,jacocoagent.jar的下载地址https://www.jacoco.org/jacoco/index.html

再次启动项目,请求call接口,日志打印情况如下:

2020-09-17 16:08:24.778  INFO 42460 --- [nio-8080-exec-1] duan.example.jacoco.util.HttpUtil        : 请求的URL:http://localhost:8080/user/createUser?1=1&username=duan&sex=lady&age=18&$jacocoData=[Z@646d0c35
2020-09-17 16:08:25.791  INFO 42460 --- [nio-8080-exec-2] o.apache.coyote.http11.Http11Processor   : Error parsing HTTP request header
 Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level.

java.lang.IllegalArgumentException: Invalid character found in the request target [/user/createUser?1=1&username=duan&sex=lady&age=18&$jacocoData=[Z@646d0c35]. The valid characters are defined in RFC 7230 and RFC 3986
	at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:491) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:260) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1589) [tomcat-embed-core-9.0.37.jar:9.0.37]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.37.jar:9.0.37]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_221]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_221]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.37.jar:9.0.37]
	at java.lang.Thread.run(Thread.java:748) [na:1.8.0_221]

可以看到,在上图第一行输出了请求的URL,在URL的后面多了一部分&$jacocoData=[Z@646d0c35,也就是这个原因,导致的我在项目中使用时调用B系统的接口出错。

那既然知道出现这个问题的原因,又不能让测试取消这个启动参数,该怎么解决呢?有以下几种办法:

(1)不使用反射函数,手动拼接URL

(2)在反射方法中,使用了是否为复合字段的field方法来判断,代码如下:

private static String getGetUrl(String url, Object obj, Class classType) {
        StringBuilder sb = new StringBuilder().append(url).append("?1=1");
        try {
            Field[] fields = classType.getDeclaredFields();
            for(Field field : fields) {
                field.setAccessible(true);
                if(field.isSynthetic()) {
                    continue;
                }
                String name = field.getName();
                Object value = field.get(obj);
                if (value == null) {
                    sb.append("&").append(name).append("=");
                } else if (value instanceof List){
                    for (Object var : ((List) value).toArray()) {
                        sb.append("&").append(name).append("=").append(var);
                    }
                } else {
                    sb.append("&").append(name).append("=").append(value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值