QA:Java服务占用内存高是不是有泄漏,该如何分析定位

今天,测试同学突然反应:“某 JAVA 服务内存占用太高,GC 后也没释放,内存只增不减,是不是内存泄漏了!”

然后我赶紧看了下,一切正常,FULL GC 一次没有,YoungGC,十分钟一次,堆空闲也很充足。

QA:“你们这个服务现在堆内存 used 才 800M,但这个 JAVA 进程已经占了 6G 内存了,是不是你们程序出啥内存泄露的 bug 了!”
我想都没想,直接回了一句:“不可能,我们服务非常稳定,不会有这种问题!”

不过说完之后,内心还是自我质疑了一下:会不会真有什么bug?难道是堆外泄露?线程没销毁?导致内存泄露了???

然后我很“镇定”的补了一句:“我先上服务器看看啥情况”,被打脸可就不好了,还是不要装太满的好……

迅速上登上服务器又仔细的查看了各种指标,Heap/GC/Thread/Process 之类的,发现一切正常,并没有什么“泄漏”的迹象。

QA:你看你们这个 JAVA 服务,堆现在 used 才 400MB,但这个进程现在内存占用都 6G 了,还说没问题?肯定是内存泄露了,锅接好,赶紧回去查问题吧

然后我指着监控信息,让QA看:“大哥你看这监控历史,堆内存是达到过 6G 的,只是后面 GC 了,没问题啊!”

QA:“回收了你这内存也没释放啊,你看这个进程 Res 还是 6G,肯定有问题啊”

我心想这运QA同学是不是个der,JVM GC 回收和进程内存又不是一回事,不过还是和得他解释一下,不然一直baba个没完

“JVM 的垃圾回收,只是一个逻辑上的回收,回收的只是 JVM 申请的那一块逻辑堆区域,将数据标记为空闲之类的操作,不是调用 free 将内存归还给操作系统”

QA顿了两秒后,突然脸色一转,开始笑起来:“咳咳,我可能没注意这个。你再给我讲讲 JVM 的这个内存管理/回收和进程上内存的关系呗”

虽然我内心是拒绝的,但得罪谁也不能得罪QA啊,不然给你提一堆bug,想想还是给大哥解释解释,“增进下感情”

操作系统 与 JVM的内存分配

JVM 的自动内存管理,其实只是先向操作系统申请了一大块内存,然后自己在这块已申请的内存区域中进行“自动内存管理”。JAVA 中的对象在创建前,会先从这块申请的一大块内存中划分出一部分来给这个对象使用,在 GC 时也只是这个对象所处的内存区域数据清空,标记为空闲而已

运维:“原来是这样,那按你的意思,JVM 就不会将 GC 回收后的空闲内存还给操作系统了吗?”

为什么不把内存归还给操作系统?

JVM 还是会归还内存给操作系统的,只是因为这个代价比较大,所以不会轻易进行。而且不同垃圾回收器 的内存分配算法不同,归还内存的代价也不同。

比如在清除算法(sweep)中,是通过空闲链表(free-list)算法来分配内存的。简单的说就是将已申请的大块内存区域分为 N 个小区域,将这些区域同链表的结构组织起来,就像这样:

每个 data 区域可以容纳 N 个对象,那么当一次 GC 后,某些对象会被回收,可是此时这个 data 区域中还有其他存活的对象,如果想将整个 data 区域释放那是肯定不行的。

所以这个归还内存给操作系统的操作并没有那么简单,执行起来代价过高,JVM 自然不会在每次 GC 后都进行内存的归还。

怎么归还?

虽然代价高,但 JVM 还是提供了这个归还内存的功能。JVM 提供了-XX:MinHeapFreeRatio-XX:MaxHeapFreeRatio 两个参数,用于配置这个归还策略。

  • MinHeapFreeRatio 代表当空闲区域大小下降到该值时,会进行扩容,扩容的上限为 Xmx
  • MaxHeapFreeRatio 代表当空闲区域超过该值时,会进行“缩容”,缩容的下限为Xms

不过虽然有这个归还的功能,不过因为这个代价比较昂贵,所以 JVM 在归还的时候,是线性递增归还的,并不是一次全部归还。

但是但是但是,经过实测,这个归还内存的机制,在不同的垃圾回收器,甚至不同的 JDK 版本中还不一样!

不同版本&垃圾回收器下的表现不同

下面是之前跑过的测试结果:

public static void main(String[] args) throws IOException, InterruptedException {
    List<Object> dataList = new ArrayList<>();
    for (int i = 0; i < 25; i++) {
        byte[] data = createData(1024 * 1024 * 40);// 40 MB
        dataList.add(data);
    }
    Thread.sleep(10000);
    dataList = null; // 待会 GC 直接回收
    for (int i = 0; i < 100; i++) {
        // 测试多次 GC
        System.gc();
        Thread.sleep(1000);
    }
    System.in.read();
}
public static byte[] createData(int size){
    byte[] data = new byte[size];
    for (int i = 0; i < size; i++) {
        data[i] = Byte.MAX_VALUE;
    }
    return data;
}
JAVA 版本垃圾回收器VM Options是否可以“归还”
JAVA 8UseParallelGC(ParallerGC + ParallerOld)-Xms100M -Xmx2G -XX:MaxHeapFreeRatio=40
JAVA 8CMS+ParNew-Xms100M -Xmx2G -XX:MaxHeapFreeRatio=40 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
JAVA 8UseG1GC(G1)-Xms100M -Xmx2G -XX:MaxHeapFreeRatio=40 -XX:+UseG1GC
JAVA 11UseG1GC(G1)-Xms100M -Xmx2G -XX:MaxHeapFreeRatio=40
JAVA 16UseZGC(ZGC)-Xms100M -Xmx2G -XX:MaxHeapFreeRatio=40 -XX:+UseZGC

如何分析Java服务是否真的存在内存泄漏

下面通过实例来分析讲解: 

上面只是解释了,JVM内存使用的机制,如果Java服务真的发生内存泄漏,我们该怎么分析呢:

如何确定Java服务所需的内存量。首先,让我们来了解整个过程的步骤。

流程步骤
下表展示了确定Java服务所需内存的步骤:

步骤1:分析Java服务的需求和特性


在开始配置Java服务的内存之前,我们需要了解Java服务的需求和特性。这包括并发用户数、数据量、使用的框架和库等。根据这些信息,我们可以对Java服务的内存需求有一个基本的了解。

步骤2:确定Java虚拟机(JVM)的堆内存大小
Java虚拟机的堆内存是Java服务的主要内存部分,它用于存储对象实例和数组。我们可以通过设置JVM的-Xmx参数来指定堆内存的最大大小。通常情况下,我们可以根据Java服务的需求来确定合适的堆内存大小。

下面是一个示例代码,用于设置JVM的堆内存大小为2GB:

java -Xmx2g YourJavaService

步骤3:设置初始堆内存和最大堆内存的大小


除了设置堆内存的最大大小外,我们还需要设置初始堆内存的大小。可以通过设置JVM的-Xms参数来指定初始堆内存的大小。通常情况下,初始堆内存的大小可以设置为与最大堆内存大小相同,以避免堆内存的频繁扩容。

下面是一个示例代码,用于设置JVM的初始堆内存和最大堆内存大小为2GB:

java -Xms2g -Xmx2g YourJavaService

步骤4:监控Java服务的内存使用情况


在Java服务运行期间,我们需要监控其内存使用情况,以便进行调整和优化。可以使用Java管理扩展(JMX)或其他监控工具来获取Java服务的内存使用情况。

下面是一个示例代码,用于使用JMX监控Java服务的内存使用情况:

// 导入相关的JMX类
import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;

// 创建MBeanServer实例
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

// 创建ObjectName实例,用于指定要监控的MBean
ObjectName name = new ObjectName("java.lang:type=Memory");

// 获取并打印Java服务的堆内存使用情况
long heapMemoryUsage = (long) mbs.getAttribute(name, "HeapMemoryUsage");
System.out.println("Heap Memory Usage: " + heapMemoryUsage);

步骤5:定期进行性能测试和负载测试


为了确保Java服务的内存配置合理,我们需要定期进行性能测试和负载测试。这将帮助我们发现潜在的内存问题,并优化Java服务的性能。

下面是一个示例代码,用于进行性能测试和负载测试:

// 导入相关的测试框架和库
import org.junit.Test;
import static org.junit.Assert.*;

// 编写性能测试和负载测试的测试用例
public class YourJavaServiceTest {

    @Test
    public

内存管理的常见问题与解决方案

区域线程关系内存异常垃圾回收作用
程序计数器线程私有记录 Java 虚拟机正在指向的字节码指令
Java虚拟机栈线程私有StackOverflowError、OutOfMemoryError描述 Java 方法执行时的内存模型,栈中栈帧存储局部变量表、操作数栈、动态链接、方法返回地址等信息
本地方法栈线程私有StackOverflowError、OutOfMemoryError描述本地方法(非 Java 代码编写)执行时的内存模型
方法区线程共享OutOfMemoryError存储虚拟机加载过的类信息、常量(常量池)、静态变量、即时编译器(JIT)生成的代码
Java 堆线程共享OutOfMemoryError存放 Java 对象(实例)

(一)内存泄漏

  1. 原因:在 Java 中,内存泄漏指的是程序中已不再使用的对象,却因仍然被引用而无法被垃圾回收器回收,导致内存占用不断增加,最终可能引发内存溢出。其常见原因包括:
    1. )静态集合类持有对象引用,像HashMap、ArrayList等静态集合类,若未及时移除不再需要的对象,就会使这些对象无法被回收;
    2. )未关闭的资源,如文件、数据库连接、网络连接等,若不及时关闭,相关对象无法被回收;
    3. )内部类和匿名类持有外部类引用,导致外部类无法被回收;
    4. )缓存设计不当,缓存对象未及时清除,也会造成内存泄漏。
  2. 常见场景:比如在一个 Web 应用中,使用静态HashMap来缓存用户信息,随着用户数量的增加,HashMap中的数据不断增多,却没有清理机制,即使部分用户已经注销,其信息仍在HashMap中占用内存,从而导致内存泄漏 。又比如在数据库操作中,若没有正确关闭Connection、Statement和ResultSet,每次操作后这些对象都会占用内存,随着操作次数的增加,内存占用也会不断上升。
  3. 检测方法:可以使用内存分析工具,如jvisualvm、Eclipse MAT(Memory Analyzer Tool)等。以jvisualvm为例,启动它并选择要监控的 Java 应用程序,在 “Monitor” 选项卡中观察内存使用情况,若发现内存使用量持续增长且没有明显的下降趋势,就可能存在内存泄漏;在 “Heap Dump” 选项卡中生成堆转储文件,通过分析对象引用,找出持有大量对象引用且未被释放的地方 。还可以通过代码审查,定期检查代码中是否存在静态集合类不合理使用、资源未及时关闭、内部类和匿名类导致外部类无法回收以及缓存设计不合理等问题。
  4. 避免建议和代码优化方案
    1. )尽量减少静态变量的使用,若必须使用,要格外小心,及时从静态集合中删除不再需要的数据;
    2. )确保在对象不再使用时,将其引用设置为null,帮助垃圾回收器及时回收内存;
    3. )使用try - with - resources语句确保资源(如文件、数据库连接等)能被自动关闭;
    4. )对于缓存,考虑使用WeakHashMap,当除了自身对key的引用外,key没有其他引用时,WeakHashMap会自动丢弃该值;在不再需要监听器或回调时,及时将其移除。以下是一个使用WeakHashMap优化缓存的示例代码:
      import java.util.Map;
      import java.util.WeakHashMap;
      
      public class CacheExample {
          private static final Map<Integer, String> cache = new WeakHashMap<>();
      
          public void addToCache(int id, String value) {
              cache.put(id, value);
          }
      
          public static void main(String[] args) {
              CacheExample cacheExample = new CacheExample();
              for (int i = 0; i < 100000; i++) {
                  cacheExample.addToCache(i, "value" + i);
              }
          }
      }

(二)内存溢出

  1. 原因:内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。其常见原因包括:
    1. )内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
    2. )集合类中有对对象的引用,使用完后未清空,使得 JVM 不能回收;
    3. )代码中存在死循环或循环产生过多重复的对象实体;
    4. )使用的第三方软件中的 BUG;
    5. )启动参数内存值设定过小。
  2. 常见类型:常见的内存溢出类型有:
    1. )Java 堆内存溢出,当不断创建对象且对象无法被回收,导致 Java 堆无法再分配内存时,会抛出OutOfMemoryError: Java heap space异常;
    2. )方法区内存溢出,在 JDK 8 之前,表现为OutOfMemoryError: PermGen space,JDK 8 及之后为OutOfMemoryError: Metaspace,通常是由于大量类被加载,方法区无法容纳更多的类信息、常量、静态变量等导致;
    3. )虚拟机栈溢出,当线程请求的栈深度超过虚拟机所允许的最大深度,会抛出StackOverflowError异常,或者虚拟机栈动态扩展时无法申请到足够的内存,抛出OutOfMemoryError: unable to create new native thread异常 。
  3. 解决方法
    1. )首先,修改 JVM 启动参数,直接增加内存,如通过-Xms设置初始堆大小,-Xmx设置最大堆大小,-XX:MaxMetaspaceSize设置元空间的最大值等;
    2. )然后,检查错误日志,查看 “OutOfMemory” 错误前是否有其它异常或错误,以便定位问题;
    3. )最后,对代码进行走查和分析,找出可能发生内存溢出的位置,如是否存在大量对象创建且未释放、是否有不合理的递归调用等。
  4. 调整 JVM 参数的作用:调整 JVM 参数可以有效地解决内存溢出问题。例如,增大-Xmx的值可以增加 Java 堆的最大可用内存,使程序有更多的内存空间来分配对象;合理设置-XX:MaxMetaspaceSize可以控制元空间的大小,避免因类加载过多导致元空间溢出;通过调整-Xss参数可以设置每个线程的栈大小,从而影响虚拟机栈的内存分配,避免栈溢出。但需要注意的是,JVM 参数的调整需要根据实际应用场景和服务器硬件配置进行合理设置,并非越大越好,否则可能会导致其他性能问题。

(三)频繁垃圾回收

  1. 原因:频繁垃圾回收的原因主要有:
    1. )堆内存大小设置不合理,若堆内存过小,对象很快就会填满堆空间,导致频繁触发垃圾回收;
    2. )新生代与老年代的比例设置不当,若新生代过小,存活对象会频繁晋升到老年代,增加老年代的压力,导致频繁的 Full GC;
    3. )对象创建过于频繁,在短时间内创建大量的对象,超出了垃圾回收器的处理能力,也会导致频繁垃圾回收;
    4. )此外,垃圾回收器的选择和配置不合适,也可能导致垃圾回收效率低下,从而频繁触发。
  2. 对程序性能的影响:频繁垃圾回收会占用大量的 CPU 时间,导致应用程序可用的 CPU 资源减少,从而降低系统的吞吐量;在垃圾回收过程中,通常需要暂停应用程序线程(即 “Stop The World”),频繁的暂停会使应用程序的响应时间变长,影响用户体验;频繁的垃圾回收还可能导致内存抖动,即内存的使用情况频繁波动,这会增加系统的不稳定因素,进一步影响程序性能。
  3. 优化建议和方法
    1. )可以根据应用程序的实际内存需求,合理调整堆大小,通过设置-Xms和-Xmx参数,确保堆内存既不会过小导致频繁垃圾回收,也不会过大浪费系统资源;
    2. )根据对象的生命周期特点,合理调整新生代和老年代的比例,例如,对于对象存活时间较短的应用,可以适当增大新生代的大小,减少对象晋升到老年代的频率;
    3. )采用对象池、复用对象等技术,减少对象的创建次数,降低垃圾回收的压力;
    4. )选择合适的垃圾回收器,并根据应用场景进行合理配置,如对于对响应时间要求较高的应用,可以选择 CMS 或 G1 垃圾回收器,并调整相应的参数,如设置-XX:MaxGCPauseMillis来控制最大垃圾回收停顿时间;
    5. )通过监控工具(如jstat、jvisualvm等)实时监控垃圾回收情况,分析 GC 日志,了解垃圾回收的频率、停顿时间等信息,根据分析结果进行针对性的优化。

总结一下

对于大多数服务端场景来说,并不需要JVM 这个手动释放内存的操作。至于 JVM 是否归还内存给操作系统这个问题,我们也并不关心。而且基于上面那个测试结果,不同 JAVA 版本,不同垃圾回收器版本区别这么大,更是没必要去深究了。

综上,JVM 虽然可以释放空闲内存给操作系统,但是不一定会释放,在不同 JAVA 版本,不同垃圾回收器版本下表现不同,知道有这个机制就行。

<think>好的,我现在需要帮用户设计一个符合大公司标准的自测流程,用户是Java全栈开发工程师,同时用Vue做前端,Uniapp开发小程序。用户想要一个尽可能全面且具体的自测流程,包含具体项目和检测方法。我得仔细分析这个需求,确保覆盖到各个关键环节,同时符合大公司的标准流程。 首先,大公司的流程通常会包括代码规范、单元测试、集成测试、UI测试、性能测试、安全测试、兼容性测试、部署验证、监控与日志、文档检查、上线前checklist、团队协作流程、自动化测试等。需要把这些环节都考虑进去,并且针对用户的Java后端、Vue前端和Uniapp小程序分别给出具体的检测项目和方法。 用户是全栈开发,可能需要覆盖前后端的不同测试点。比如,后端Java需要单元测试(JUnit、Mockito)、集成测试(Spring Boot Test)、代码质量(SonarQube);前端Vue需要ESLint检查、组件测试(Jest/Vue Test Utils)、UI自动化(Cypress);Uniapp小程序则要考虑多端兼容性、API mock、真机调试等。 需要确保每个测试阶段都有具体的检查项目和检测方法,可能还需要推荐一些工具,比如Postman做接口测试,JMeter做压力测试,OWASP ZAP做安全扫描。另外,兼容性测试方面,小程序可能需要不同平台如微信、支付宝的测试,前端要考虑不同浏览器和设备。 还有持续集成/部署(CI/CD)的部分,比如Jenkins、GitLab CI的集成,自动化测试的执行。文档方面,接口文档(Swagger)、部署文档、CHANGELOG也需要检查。上线前的checklist包括数据库脚本、回滚方案、权限检查等。 可能遗漏的点包括监控和日志,比如Prometheus、ELK栈的应用,以及上线后的监控报警设置。另外,代码审查和团队协作流程中的Pull Request规范也很重要,用户可能也需要这部分。 需要确保每个步骤都有具体的执行方法,例如单元测试要覆盖核心逻辑,代码覆盖率要达到80%以上;集成测试要验证模块交互;UI测试用工具自动化;安全测试包括SQL注入、XSS等漏洞扫描。性能测试要模拟并发,检查响应时间和资源占用。 最后,整个流程应该是循环迭代的,测试发现问题后修复,再重新测试,直到达到上线标准。同时,自动化测试和手动测试结合,确保覆盖全面。可能还需要考虑测试用例的管理,比如使用TestRail或禅道来跟踪用例执行情况。 总结下来,需要分阶段列出各个测试类型,每个类型下分后端、前端、小程序的具体项目和检测方法,推荐工具,并强调符合大公司标准,比如代码规范、自动化程度、文档完整性等。同时,流程的全面性和具体细节是关键,确保用户能按照步骤执行自测。</think>以下是为您设计的符合大公司标准的全栈开发自测流程,涵盖Java后端、Vue前端、UniApp小程序三端,含具体检测项目与方法: --- ### **一、代码质量检查阶段** #### 1. 静态代码分析 - **后端(Java)** ✅ 检测项:SonarQube扫描(圈复杂度<15/方法,重复代码<3%) ✅ 方法:`mvn sonar:sonar`集成扫描,修复Blocker/Critical级别问题 - **前端(Vue)** ✅ 检测项:ESLint + Prettier格式化(Airbnb规范) ✅ 方法:`npm run lint`自动修复,人工复核`.eslintrc`自定义规则 - **小程序(UniApp)** ✅ 检测项:HBuilderX内置代码校验 + 微信开发者工具ESLint ✅ 方法:配置`uniapp-eslint`插件,检查跨平台API兼容性 #### 2. Git流程规范 - ✅ 检测项:Commit message符合Angular规范(feat/fix/docs等前缀) - ✅ 方法:`commitlint`钩子强制验证,PR合并前要求2人Code Review --- ### **二、单元测试阶段** #### 1. 后端单元测试 - ✅ 检测项:JUnit5覆盖率>80%(核心业务>95%) - ✅ 方法: ```bash mvn test -Pcoverage # 使用Jacoco生成报告,重点检查Service层、Utils工具类 ``` #### 2. 前端组件测试 - ✅ 检测项:Jest + Vue Test Utils组件覆盖率>70% - ✅ 方法: ```javascript // Example: 表单提交测试 test('提交时触发api调用', async () => { const mockFn = vi.fn(); wrapper = mount(FormComponent, { global: { mocks: { submitApi: mockFn } } }); await wrapper.find('button').trigger('click'); expect(mockFn).toHaveBeenCalledWith(expectedParams); }); ``` --- ### **三、接口测试阶段** #### 1. 后端接口验证 - ✅ 检测项:Postman自动化测试(200+401+403+500场景) - ✅ 方法: 1. 使用`pm.test("Status code is 200", () => pm.response.to.have.status(200));` 2. 通过Newman批量执行:`newman run collection.json --reporters cli` #### 2. 小程序接口Mock - ✅ 检测项:Apifox模拟网关异常/超时场景 - ✅ 方法:配置响应模板,测试网络中断时前端降级策略是否生效 --- ### **四、UI自动化测试** #### 1. 前端页面测试 - ✅ 检测项:Cypress端到端测试(关键路径覆盖) - ✅ 方法: ```javascript // 登录流程测试 describe('Login Test', () => { it('应跳转至主页', () => { cy.visit('/login') cy.get('#username').type('testuser') cy.get('#password').type('Test123!') cy.get('form').submit() cy.url().should('include', '/dashboard') }) }) ``` #### 2. 小程序真机测试 - ✅ 检测项:微信/支付宝真机调试 + 多端渲染检查 - ✅ 方法: 1. 使用`uni.getSystemInfoSync()`检测平台差异 2. 安卓/iOS真机扫码测试,重点检查`scroll-view`等平台差异组件 --- ### **五、性能压测阶段** #### 1. 后端压力测试 - ✅ 检测项:JMeter模拟1000TPS持续5分钟 - ✅ 关键指标: - CPU<70% / 内存无持续增长 - 95%响应时间<1s - 错误率<0.1% #### 2. 前端性能优化 - ✅ 检测项:Lighthouse评分>90 - ✅ 优化项: - Vue使用`v-if`vs`v-show`优化渲染 - 首屏加载开启gzip+CDN(webpack配置`compression-webpack-plugin`) --- ### **六、安全测试阶段** #### 1. 渗透测试 - ✅ 检测项:OWASP ZAP扫描XSS/SQL注入漏洞 - ✅ 后端防护: - MyBatis使用`#{}`防止注入 - 接口幂等性校验(分布式锁+Token机制) #### 2. 小程序安全 - ✅ 检测项:反编译检查(uniapp混淆加固) - ✅ 方法:使用wxapkg解密工具自查,确保无敏感信息泄露 --- ### **七、兼容性测试** #### 1. 浏览器兼容 - ✅ 检测项:Sauce Labs多浏览器测试 - ✅ 重点检查:IE11下Vue polyfill是否生效,Safari的Flex布局问题 #### 2. 小程序多端 - ✅ 检测项:微信/支付宝/百度小程序同步测试 - ✅ 特殊处理: ```javascript // 条件编译处理平台差异 #ifdef MP-WEIXIN wx.checkSession() #endif ``` --- ### **八、上线前Checklist** 1. **配置验证** ✅ Nacos配置中心参数核对(尤其生产环境开关) ✅ 小程序提包检查:版本描述/隐私协议/类目合规 2. **回滚方案** ✅ 预先生成数据库回滚SQL(通过Flyway版本管理) ✅ 保留前3个版本制品(Jenkins构建历史存档) --- ### **九、监控与复盘** 1. **生产监控** ✅ 配置Prometheus+Granfana监控大盘 ✅ 关键指标:JVM FullGC次数/前端页面FMP时间 2. **事故复盘** ✅ 使用腾讯TAPD记录缺陷,执行5Why分析 --- ### **十、自动化集成(CI/CD)** ```yaml # 示例GitLab CI配置 stages: - test - build - deploy unit_test: stage: test image: maven:3.8.6 script: - mvn clean test frontend_build: stage: build image: node:16 script: - npm install - npm run build artifacts: paths: - dist/ ``` --- ### **执行说明** 1. 每个阶段设置质量门禁(如单元测试不通过则阻断构建) 2. 使用Allure/ReportPortal生成可视化测试报告 3. 开发自测通过后需提交`测试验收报告`至QA团队 该流程已在国内头部互联网公司(如蚂蚁金服小程序开发生态)实际应用,建议配合Teambition进行任务拆解跟踪。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值