不期速成 日拱一卒 不期速成 日拱一卒
首页
技术
测试
分类
标签
归档
关于
首页
技术
测试
分类
标签
归档
关于
  • 基础

  • 进阶

  • 探索

    • 测试的有效性
    • 测试有效性探索之测试执行与代码覆盖率
      • 1 引言
      • 2 测试执行与代码覆盖率的关系
        • 2.1 测试执行的定义
        • 2.2 代码覆盖率的定义
        • 2.3 二者的关系
        • 2.4 代码覆盖率的意义和价值
      • 3 代码覆盖率工具介绍
        • 3.1 技术选型
        • 3.2 覆盖率工具工作流程
        • 3.3 插桩原理
        • 3.4 JaCoCo简介
      • 4 使用JaCoCo开展代码覆盖率探索实践
        • 4.1 小试牛刀
        • 4.2 单元测试代码覆盖率探索
        • 4.3 推而广之,更大的应用场景
      • 5 进一步海阔天空
        • 5.2 走向精准测试
    • 测试覆盖度量全景图
    • Prism测试覆盖度量
    • 精准测试探索
  • 把你的测试用例当作一幅画(邰晓梅)
  • 测试
  • 探索
2021-01-04

测试有效性探索之测试执行与代码覆盖率

# 1 引言

近期在讨论与思考,如何客观的评估测试有效性,相关讨论思考内容的已经由姜越撰写成文《测试的有效性》,请先移步此文阅读思考后再往下看。

接下来我们会讨论在测试覆盖率度量方向的一些探索。

# 2 测试执行与代码覆盖率的关系

# 2.1 测试执行的定义

此处的“测试执行”是广义上的测试执行,泛指各类从测试设计的思考成果出发,通过各种形式对被测系统进行“输入”操作的一系列活动。

从这个定义上来讲,包括了UI层的测试、接口层的测试、单元方法层的测试,从执行方式上包括手工、自动化等。

# 2.2 代码覆盖率的定义

代码覆盖率,是一种通过计算测试过程中被执行的源代码占全部源代码的比例,进而间接度量软件质量的方法,按性质,它属于白盒测试的范畴。

# 2.3 二者的关系

通过测试执行,来触发相关被测系统代码的执行,借助工具对被测系统进行代码层面的测试覆盖的统计和分析,在一定程度上可以将代码覆盖率与测试覆盖率建立一种非严格意义上的等价的映射关系。

代码覆盖率 ≈ 测试覆盖率

为什么是约等于?举个例子:

if x > 0
	y = 5 / x   // 没有除零错误
else 
	y = 5 / (x + 1)  // x = -1 的时候,会出现除零错误
1
2
3
4

很简单的例子,显然的两个x输入参数(映射为2条测试用例)即可做到代码的完全覆盖,x = 2和x = -2即可,但这对找到x = -1触发除零错误没有用,以小见大其结论是:

  • 高覆盖的测试用例 ≠ 测试用例有用
  • 没有覆盖的分支 == 该分支上的任何错误肯定都测不到,注意错误不限于各种异常

# 2.4 代码覆盖率的意义和价值

如果代码覆盖率高,那么产品质量一定好吗?

答案是否定的。

影响产品质量的因素很多,代码覆盖率高只是保障产品质量的一个必要条件,代码覆盖率高不能说明产品质量好,但反过来,代码覆盖率低,产品质量很难得到有效保障。代码全覆盖仅仅是覆盖了基线,一味的追求代码全覆盖,并不意味着搞出了有用的测试用例。

那么度量代码覆盖率(测试覆盖率)的价值是什么?

个人认为有以下价值:

  1. 分析未覆盖的部分代码,从而反推测试设计是否充分,没有覆盖到的代码是否是测试设计的盲点;
  2. 检测出程序中的废弃代码,可以逆向反推在代码设计中思维混乱点,提醒设计/开发人员理清代码逻辑关系,提升代码质量;

# 3 代码覆盖率工具介绍

# 3.1 技术选型

我司的核心技术栈是Java,因此我们从Java开始。常见的Java代码覆盖工具有JaCoCo、Emma和Cobertura,三者的对比如下:

JaCoCo Emma Cobertura
原理 使用ASM修改字节码 可以修改jar文件、class文件字节码文件 基于Jcoverage,基于ASM框架对class插桩
覆盖粒度 方法、类、行、分支、指令、圈 行、块、方法、类 行、分支
插桩 on-the-fly和offline on-the-fly和offline offline
缺点 不支持JDK1.8(已停止维护) 退出JVM才能获取覆盖率报告
性能 快 较快 较快

毫无疑问的,我们选择JaCoCo。

# 3.2 覆盖率工具工作流程

image-20210104175624516

  1. 对JAVA字节码进行插桩,On-The-Fly和Offline两种方式;
  2. 执行测试用例,手机程序执行轨迹信息,将其dump到内存;
  3. 数据处理器结合程序执行轨迹信息和代码结构信息分析生成代码覆盖率报告;
  4. 将代码覆盖率报告图形化形式展示出来,如html、xml等文件格式。

# 3.3 插桩原理

image-20210105083745755

主流代码覆盖率工具都采用字节码插桩模式,通过钩子的方式来记录代码执行轨迹信息。其中字节码插桩又分为两种模式On-The-Fly和Offline。

# 3.3.1 On-The-Fly插桩Java Agent

  • JVM中通过-javaagent参数指定特定的jar文件启动Instrumentation的代理程序;
  • 代理程序在每装载一个class文件前判定是否已经转换修改了该文件,如果没有则需要将探针插入class文件中;
  • 代码覆盖率就可以在JVM执行代码的时候实时获取

# 3.3.2 On-The-Fly插桩Class Loader

  • 自定义classloader实现自己的类装载策略,在类加载之前将探针插入class文件中

# 3.3.3 Offline插桩

  • 在测试之前对文件进行插桩,生成插过桩的class文件或jar包,执行插过桩的class文件或jar包之后,会生成覆盖率信息到文件,最后统一对覆盖率信息进行处理,并生成报告

# 3.3.4 On-The-Fly和Offline的对比

On-The-Fly模式优点在于无需修改源代码,可以在系统中不停机的情况下,实时收集代码覆盖率信息;Offline模式优点在于系统启动不需要额外开启代理,但是只能在系统停机的情况下才能获取代码覆盖率。

Offline模式适用于以下场景:

  • 运行环境不支持java agent;
  • 部署环境不允许设置JVM参数;
  • 字节码需要被转换成其他虚拟机字节码,如Android Dalvik VM;
  • 动态修改字节码过程中和其他agent冲突;
  • 无法自定义用户加载类

# 3.4 JaCoCo简介

JaCoCo是我们选型的代码覆盖率工具,先简单了解下这个工具。

JaCoCo官网介绍其最新发行版包含如下库:

image-20210105090014409

我们通常使用jacocoagent.jar和jacococli.jar即可。

使用jacocoagent.jar用于插桩记录代码执行轨迹,jacococli.jar用于处理exec文件,生成报告等。

# 3.4.1 jacocoagent.jar

关于jacocoagent.jar的官方说明如下:

The JaCoCo agent collects execution information and dumps it on request or when the JVM exits. There are three different modes for execution data output:

  • File System: At JVM termination execution data is written to a local file.
  • TCP Socket Server: External tools can connect to the JVM and retrieve execution data over the socket connection. Optional execution data reset and execution data dump on VM exit is possible.
  • TCP Socket Client: At startup the JaCoCo agent connects to a given TCP endpoint. Execution data is written to the socket connection on request. Optional execution data reset and execution data dump on VM exit is possible.

The agent jacocoagent.jar is part of the JaCoCo distribution and includes all required dependencies. A Java agent can be activated with the following JVM option:

  -javaagent:[yourpath/]jacocoagent.jar=[option1]=[value1],[option2]=[value2]
1

image-20210105091955904

# 3.4.2 jacococli.jar

关于jacococli.jar的官方说明如下:

JaCoCo comes with a command line interface to perform basic operations from the command line. The command line tools with all dependencies are packaged in jacococli.jar and are available with the JaCoCo download. Java 1.5 or greater is required for execution.

For more sophisticated usage especially with larger projects please use our integrations with various build tools.

The following commands are available. Each command has a list of optional and required parameters. Some parameters can be specified multiple times to provide multiple values.

Warning: Although a instrument command is provided the preferred way for code coverage analysis with JaCoCo is on-the-fly instrumentation with the JaCoCo agent. Offline instrumentation has several drawbacks and should only be used if a specific scenario explicitly requires this mode. Please consult documentation about offline instrumentation before using this mode.

我们常用的参数列举如下

# 3.4.2.1 dump
java -jar jacococli.jar dump [--address <address>] --destfile <path> [--help] [--port <port>] [--quiet] [--reset] [--retry <count>]
1

image-20210105092139960

# 3.4.2.2 report
java -jar jacococli.jar report [<execfiles> ...] --classfiles <path> [--csv <file>] [--encoding <charset>] [--help] [--html <dir>] [--name <name>] [--quiet] [--sourcefiles <path>] [--tabwith <n>] [--xml <file>]
1

image-20210105092222201

# 3.4.2.3 merge
java -jar jacococli.jar merge [<execfiles> ...] --destfile <path> [--help] [--quiet]
1

image-20210105092306968

# 4 使用JaCoCo开展代码覆盖率探索实践

# 4.1 小试牛刀

在2019年探索SDK测试的过程中,我曾经开发过一款工具NexusMavenUploader (opens new window),此工具使用Java语言开发,命令行启动,执行完毕退出,我们使用此工具来做个试验,体验下JaCoCo。

jar包运行方式以及命令提示信息如下:

image-20210105091235060

假设,我们只编写了一条测试用例,传入一个jarFilesPath目录参数,其运行效果如下

image-20210105091605916

# 4.1.1 使用Java agent插桩运行

因为该程序运行结束后即退出,我们选用本地生成exec文件的方式进行插桩dump代码执行轨迹数据,其命令行如下:

java -javaagent:F:\JaCoco\jacoco-0.8.6\lib\jacocoagent.jar=includes=com.qguo,output=file,destfile=F:\JaCoco\result\demo2.exec -jar F:\JaCoco\demo\NexusMavenUploader-1.2.1.jar F:\JaCoco\jacoco-0.8.6\lib
1

解释说明:

  • -javaagent用于指定jacocoagent.jar包的访问路径;
  • includes用于指定分析的类名前缀,支持正则和多组类名,如不填默认为所有类名;
  • output为输出模式,此案例选择为file,直接输出到本地,通过destfile参数指定输出存储路径;

image-20210105093035905

插桩执行此命令后,程序正常运行完毕,我们获得了一个demo2.exec文件,此文件记录了JVM内程序的运行轨迹信息:

image-20210105093306395

# 4.1.2 生成覆盖率报告

生成完整的覆盖率报告需要3个文件:

  • 记录程序运行轨迹的exec文件;
  • 插桩对象的Java class文件,可以是jar包;
  • 插桩对象的Java源码,用于代码染色查看【可选参数】;

其命令示例如下:

java -jar F:\JaCoco\jacoco-0.8.6\lib\jacococli.jar report F:\JaCoco\result\demo2.exec --classfiles F:\JaCoco\demo\NexusMavenUploader-1.2.1.jar --sourcefiles O:\GITLAB\NexusMavenUploader\src\main\java --encoding UTF-8 --html F:\JaCoco\result\reports
1

image-20210105093914611

此时我们已经生成了JaCoCo代码覆盖率报告

image-20210105093951860

# 4.1.3 查看代码覆盖率报告

image-20210105094510918

覆盖率表头字段解读:

  • Instructions:Java字节指令的覆盖率。执行的最小单位,和代码的格式无关。
  • Branches:分支覆盖率。注意:异常处理不算做分支。
  • Cxty(Cyclomatic Complexity):圈复杂度,JaCoCo会为每一个非抽象方法计算圈复杂度,并为类、包以及组计算复杂度。圈复杂度简单的说就是为了覆盖所有的路径,索要执行单元测试数量,圈复杂度大说明代码可能质量低且难于测试和维护。
  • Lines:行覆盖率,只要本行有一条指令被执行,则本行则被标记为被执行。
  • Methods:方法覆盖率,任何非抽象的方法,只要有一条指令被执行,则该方法被计为执行。
  • Classes:类覆盖率,所有类,包括接口,只要其中一个方法被执行,则标记为被执行。注意:构造函数和静态初始化块也算作方法。

由于NexusMavenUploader-1.2.1.jar没有定义package,指定的includes=com.qguo参数没有生效,默认使用了include=*参数,对所有类名进行了代码覆盖率检测,可以看到使用了第三方库被检测,其代码覆盖率很低,我们的核心代码default、utils、bean其字节指令覆盖率分别为69%、71%、100%。

我们查看下具体的覆盖率情况

image-20210105094546660

查看源码覆盖染色标记

image-20210105101444436

源码染色图例解读

钻石代表分支覆盖情况:

  • 红色钻石:这一行没有分支被执行;
  • 黄色钻石:这一行中只有部分分支被执行;
  • 绿色钻石:这一行的所有分支都被执行。

背景色代表指令覆盖率:

  • 红色背景:这一行并没有任何指令被执行;
  • 黄色背景:这一行部分指令被执行;
  • 绿色背景:这一行的所有指令都被执行了。

# 4.1.4 感悟

经过代码覆盖率的统计以及源码的覆盖染色标记分析,我们可以清晰的看到,有些代码逻辑没有被执行到,这些逻辑需要我们根据实际情况,结合风险来优化完善测试设计,增加“测试执行”进行有效覆盖。这是我们以往仅仅根据需求文档、设计文档、UI交互图、操作被测系统、查看日志等活动所无法直观感知的,这是白盒的精准!

下面我们举几个例子来说明下:

# 4.1.4.1 测试设计优化点1

image-20210105101816603

  • 需要完善各类参数输入的测试用例

    我们只覆盖了1个参数(jarFilesPath)的场景,对于url和respositoryId这2个参数没有覆盖,且没有测试不输入任何参数的场景,需要补充测试用例

image-20210105101920789

  • 对于url参数,还要判定url是否为http开头
# 4.1.4.2 测试设计优化点2

image-20210105102438099

该工具可以根据操作系统自动的生成对应的命令行脚本,不同操作系统有不同的脚本生成分支路径,我们需要在不同操作系统平台进行测试,增加测试场景。

# 4.1.4.3 测试设计优化点3

image-20210105102720351

对于写入文件保存的异常场景均未进行有效覆盖,需要构造异常场景,来触发文件写入存储失败的场景。

# 4.2 单元测试代码覆盖率探索

2020年我们实现了SDK单元测试的从0到1的突破,Java单元测试框架我们使用TestNG,因此我们对NexusMavenUploader项目编写一些单元测试代码用于试验JaCoCo如何结合Maven、TestNG使用。为了便于编写单元测试用例,我们对NexusMavenUploader项目的Main类进行了拆分重构。

请注意,此处为了简单明了的示例,没有使用Wady绿洲单元测试框架,而是使用原生的TestNG进行单元测试,并且没有将测试逻辑和测试数据进行解耦,请不要效仿这种用用例编写模式。

对于输入参数这个测试点,我们编写4条测试用例,示例如下:

package com.test;

import com.qguo.Entry;
import org.testng.annotations.Test;
import java.util.HashMap;
import static org.testng.Assert.*;

public class TestArgument {

    /**
     * 没有输入参数则,参数HashMap应该为空
     */
    @Test
    public static void TestNoArgument() {
        String[] emptyArgs = {};
        HashMap<String, String> argsMap = Entry.judgeArgument(emptyArgs);
        assertTrue(argsMap.isEmpty(), "未输入有效参数,HashMap应当为空");
    }

    @Test
    public static void TestOnlyHavePathArg() {
        String testJarFilesPath = "/src/test/resources/testJars";
        String[] oneArgs = {testJarFilesPath};
        HashMap<String, String> argsMap = Entry.judgeArgument(oneArgs);
        assertEquals(argsMap.size(), 1);
        assertEquals(argsMap.get("jarFilesPath"), testJarFilesPath);
        assertNull(argsMap.get("url"), "应当没有URL地址");
        assertNull(argsMap.get("repositoryId"), "应当没有仓库ID");
    }

    @Test
    public static void TestHavePathAndUrlArg() {
        String testJarFilesPath = "/src/test/resources/testJars";
        String url = "http://172.17.26.6:9000/repository/maven-public/";
        String[] oneArgs = {testJarFilesPath, url};
        HashMap<String, String> argsMap = Entry.judgeArgument(oneArgs);
        assertEquals(argsMap.size(), 2);
        assertEquals(argsMap.get("jarFilesPath"), testJarFilesPath);
        assertEquals(argsMap.get("url"), url);
        assertNull(argsMap.get("repositoryId"), "应当没有仓库ID");
    }

    @Test
    public static void TestHaveAllArg() {
        String testJarFilesPath = "/src/test/resources/testJars";
        String url = "http://172.17.26.6:9000/repository/maven-public/";
        String repositoryId = "FHNexus";
        String[] oneArgs = {testJarFilesPath, url, repositoryId};
        HashMap<String, String> argsMap = Entry.judgeArgument(oneArgs);
        assertEquals(argsMap.size(), 3);
        assertEquals(argsMap.get("jarFilesPath"), testJarFilesPath);
        assertEquals(argsMap.get("url"), url);
        assertEquals(argsMap.get("repositoryId"), repositoryId);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

# 4.2.1 配置pom.xml引入JaCoCo

JaCoCo有成熟的Maven插件,我们只需参照官网文档配置即可,其配置示例如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <groupId>com.qguo</groupId>
    <artifactId>NexusMavenUploader</artifactId>
    <version>1.2.1</version>
    <dependencies>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.9.9</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>2.1.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.16</version>
                <configuration>
                    <argLine>${jacocoArgLine}</argLine>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.2</version>
                <executions>
                    <execution>
                        <id>default-prepare-agent</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                        <configuration>
                            <propertyName>jacocoArgLine</propertyName>
                        </configuration>
                    </execution>
                    <execution>
                        <id>default-prepare-agent-integration</id>
                        <goals>
                            <goal>prepare-agent-integration</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>default-report</id>
                        <goals>
                            <goal>report</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>default-report-integration</id>
                        <goals>
                            <goal>report-integration</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>default-check</id>
                        <goals>
                            <goal>check</goal>
                        </goals>
                        <configuration>
                            <rules>
                                <rule>
                                    <element>BUNDLE</element>
                                    <limits>
                                        <limit>
                                            <counter>COMPLEXITY</counter>
                                            <value>COVEREDRATIO</value>
                                            <minimum>0.0</minimum>
                                        </limit>
                                    </limits>
                                </rule>
                            </rules>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>2.16</version>
                <executions>
                    <execution>
                        <id>default-integration-test</id>
                        <goals>
                            <goal>integration-test</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-project-info-reports-plugin</artifactId>
                <version>3.0.0</version>
            </plugin>
        </plugins>
    </build>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

# 4.2.2 使用Maven执行测试用例并生成JaCoCo代码覆盖率报告

执行命令如下:mvn clean test jacoco:report

其命令行输出如下:

image-20210105104516632

image-20210105104529356

默认target/site目录下即为JaCoCo代码覆盖率报告

image-20210105104722951

# 4.2.3 代码覆盖率报告分析

image-20210105104833323

可以看到,此处没有其他第三方库的代码覆盖率信息了,只有我们的项目代码,非常干净整洁。

image-20210105105100863

我们的4条测试用例,覆盖了参数的4种情况,但对于url参数的格式依然没有进行有效覆盖,缺少异常场景:url不以http开头的非法测试输入。

当然,通过我们对源码的阅读和理解,可以判断,这种异常场景不会触发程序bug,当前代码设计场景下,风险较小,可以根据成本决定是否要增加此测试设计。

# 4.3 推而广之,更大的应用场景

上述两案例,对于我司大部分项目的不够适用,我司绝大多数项目是Web项目,我们接下来试验下这种主流场景如何实时代码覆盖率。

我们以部署在Tomcat容器内的web服务为例进行试验。

开始动手前,先想一下,对部署在Tomcat内的web服务进行插桩和上述两案例的最大不同是什么?

运行状态。

Tomcat会一旦启动,会持续运行,持续提供服务,我们不能等到Tomcat停止时再获取代码运行轨迹信息。

此时,应当使用代码覆盖数据的其他输出方式:

  • file: At VM termination execution data is written to the file specified in the destfile attribute.
  • tcpserver: The agent listens for incoming connections on the TCP port specified by the address and port attribute. Execution data is written to this TCP connection.
  • tcpclient: At startup the agent connects to the TCP port specified by the address and port attribute. Execution data is written to this TCP connection.
  • none: Do not produce any output.

简单起见,我们直接使用tcpserver模式即可

# 4.3.1 插桩获取实时代码覆盖数据

感谢大数据应用测试团队提供的试验环境

以大数据应用事业群的SYH项目为试验对象,其项目代码均再com.nuts包内,Tomcat服务器IP为172.16.44.57,我们将其Tomcat的JVM启动参数配置如下:

-javaagent:/home/qguo/lib/jacocoagent.jar=includes=com.nuts.*,output=tcpserver,port=6300,address=172.16.44.57,append=true
1

在catalina.sh文件内新增如下配置项,启动Tomcat即可。

image-20210105115608272

此时我们只需和以往一样,操作对应的业务系统,执行手工测试用例、自动化测试用例等,待测试完成即可进行收集测试执行所触发的代码覆盖数据了,当然也可以在测试过程中随时采集。

# 4.3.2 采集代码覆盖数据并生成代码覆盖报告

# 4.3.2.1 dump插桩内存数据

使用jacococli.jar工具来dump插桩记录的代码执行轨迹信息。

执行命令和参数示例如下:

java -jar F:\JaCoco\jacoco-0.8.6\lib\jacococli.jar dump --address 172.16.44.57 --destfile F:\JaCoco\result\syh.exec --port 6300
1

image-20210105120822786

# 4.3.2.2 生成代码覆盖率报告

参数命令示例如下:

java -jar F:\JaCoco\jacoco-0.8.6\lib\jacococli.jar report F:\JaCoco\result\syh.exec --classfiles O:\SVN_PATH\DSJ\SYH\DSJ-GPLH-Commence-1.4.jar --sourcefiles O:\SVN_PATH\DSJ\SYH\Code\src --encoding UTF-8 --html F:\JaCoco\result\syh_report
1

image-20210105121027341

# 4.3.2.3 查看代码覆盖率报告

image-20210105121133027

对应各业务模块的映射关系:

image-20210105142058645

查看代码染色详情示例

image-20210105121451762

从代码覆盖染色标识可以清晰的知道,“如果没有下发单位;则给13个地市发送”这一个分支的代码没有被覆盖,我们需要结合实际情况分析是否应当补充相应的测试设计来完成覆盖。

# 5 进一步海阔天空

经过本次探索,我们验证了代码覆盖率的可行性,通过代码覆盖率来反推测试覆盖,评估测试设计有效性,指导完善补充测试设计。

通过测试覆盖率工具的引入,以较低的成本打开白盒测试的门缝,将我们的测试由基本纯黑盒逐渐向部分灰盒过渡。

目前只是仅仅完成了一个简单的尝试,整个过程都需要手动执行,没有实现快速易于上手的工具整合;每次的代码覆盖率数据都没有持久化存储,无法版本间、迭代间的持续跟踪比对分析,需要与测试技术平台化服务进行对接;需要建立相应的规范制度,推进代码覆盖率的工作落地和实施,推动测试设计和测试执行的有效性度量;需要根据代码覆盖率发现的风险,提供有效的指导建议测试工程师进行测试设计优化完善……。

大胆设想,顺着这条路,还有很多有意义的事情可以做。

  • 在评估测试设计的有效性这件事上我们有了一把利器,可以客观公正无感知、低成本的进行评估,进而推动部门测试设计的严谨性和完备性能力提升;
  • 基于代码提交diff的增量测试,可以大大缩小测试范围,有的放矢,集中火力对版本或迭代的修改内容进行测试,提高测试的精准性,降低测试投入成本;
  • 在SonarQube、FlyCD中加入代码覆盖率,设定阈值标准,为CICD的有效性增加度量方式;
  • 对核心项目的核心模块设定代码覆盖率阈值标准,保障测试覆盖度,守好产品质量准成的关口;
  • ……

但这些都是一个个点的思考,体系化,全盘考虑,如何升级我们的测试工作,不由的将目光投向了当下已经在一线大厂落地开花结果的测试技术---------精准测试。

# 5.2 走向精准测试

# 5.2.1 非精准测试的局限性

传统测试存在以下6个方面的局限性,我们不展开讲述了:

  • 虽然测试流程很规范,但软件质量还是不如意;
  • 软件项目验收缺少好的运行检测手段,检测结果缺少技术公信力;
  • 传统的手工测试,测试执行无法精准量化控制,测试效率比较低;
  • 测试人员不能精准把握缺陷现场,与开发人员协同工作困难;
  • 分布式、微服务架构,软件越来越复杂,测试挑战性越来越大。

# 5.2.2 精准测试的核心

精准测试的定义:一种可以追溯的软件测试技术。

从字面理解,精准就是非常准确,非常准确需要用数字说话。

在测试领域,精准测试是一套计算机测试辅助分析系统,对测试过程的活动进行监控,讲采集到的监控数据进行分析,得到精准测试的量化数据,使用这些量化数据进行质量评价,利用这些分析数据可以崔进测试过程不断完善,形成度量及分析闭环。

精准测试的核心就是“数据与追溯”

image-20210111094843744

精准测试的核心思想就是使用非常精确和智能的软件来解决软件测试的问题,从根本上引领从经验型方法向技术型方法的转型。质量的评估不再靠经验,而是通过精准的数据来判定。

精准测试没有改变传统的软件测试方法,区别只在于,由软件去采集测试执行触发的代码逻辑及测试数据的过程,自动建立测试用例与程序代码之间的逻辑关系。在测试过程中加入软件的采集过程,可以形成正向和逆向的追溯。

通过正向追溯,开发人员可以看到测试人员执行用例的代码细节,以方便进行缺陷的修复,测试数据可以直接为开发调试提供依据,快速定位并修复缺陷。

通过逆向追溯,测试人员通过修改的源码快速确定测试用例的范围,极大减少回归测试的盲目性和工作量,快速修订测试用例,达到测试覆盖率最大化。

# 5.2.3 从精准测试的角度来看JaCoCo的局限性

本文探索讨论的Jacoco测试工具本质上还是一个白盒测试工具,其应用模式是部署在后台,采集所有执行代码的覆盖数据统计覆盖率,所有用户请求和功能的覆盖数据混合统计,范围仅围绕在看覆盖率上。这种覆盖信息的弊端是:它从全系统的维度来统计,导致粒度太大,无法详细定位和深度分析覆盖率的真正问题。


其他关于精准测试的讨论,我们单独再开一篇,此处不再展开。

这是一次有意义的尝试,通过代码覆盖为起点,我们可以打开很多新的工作思路,踏踏实实的提高测试的专业性和严谨性,希望有志之士一起来进行探索。

#白盒测试#黑盒测试#代码覆盖#测试覆盖#数字化测试
上次更新: 7/26/2026, 3:17:15 PM
测试的有效性
测试覆盖度量全景图

← 测试的有效性 测试覆盖度量全景图→

最近更新
01
测试覆盖度量全景图
11-30
02
Prism测试覆盖度量
11-30
03
AITDBClient使用文档
11-15
更多文章>
Theme by Vdoing | Copyright © 2021-2026 toddlerya | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式