专栏首页7DGroup走进Java接口测试之解决超大文本数据驱动报OOM问题

走进Java接口测试之解决超大文本数据驱动报OOM问题

前言

上篇文章 走进Java接口测试之测试框架TestNG数据驱动(入门篇)阐述测试框架 TestNG 中的一些基本的概念和玩法,本文带着大家来解决一个实际的工程问题。

问题分析

现象

使用文本做数据驱动的时候出现 JVM Heap 区 OOM。

原因

核实下 IDEA 的 JVM 参数设置,JVM 最大可用内存为 2G:

考虑到参数化文件大概有 20 万条记录,判断这是由于程序一次性读取大量的文本数据导致的。

解法

这时候我们想到测试框架 TestNG 有为这种大量数据驱动场景提供解法,即:延迟数据提供者

有的场景我们需要大量参数进行读取,比如参数数据源是 DB,而数据达到百万级,这样测试程序遍历所有数据时,可能就会导致内存溢出。那么我们怎样解决这个问题?当我们获取了一条数据,对它执行测试方法,然后就废弃这个数据对象,再测试下一个。这个原则是延迟初始化,这个思想就是当你真正需要一个对象时才创建它,而不是提前创建它。

具体实现

为了实现这种延迟加载的方法,TestNG 允许我们从数据提供者返回一个 Iterator 对象,而不是一个二维对象数组。

Iterator 是 java.util 包中的一个接口,它的方法签名如下:

public interface Iterator<E> {
    boolean hasNext();    E next();    default void remove();
 }

它可以通过 next 调用下一组数据,这样就有机会在最后一刻实例化相应的对象,即刚好在需要在这些参数的测试方法被调用之前。这样的好处是不用把所有的测试数据都加载到内存中,而是需要的时候就读一条。

首先配置 maven 依赖包:

   <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter</artifactId>        </dependency>        <!--引入 testng 测试框架-->        <dependency>            <groupId>org.testng</groupId>            <artifactId>testng</artifactId>            <version>6.14.3</version>            <scope>compile</scope>        </dependency>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>            <optional>true</optional>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>            <exclusions>                <exclusion>                    <groupId>org.junit.vintage</groupId>                    <artifactId>junit-vintage-engine</artifactId>                </exclusion>            </exclusions>        </dependency>    </dependencies>

然后实现 Iterator 接口,用于从文件中读取数据,返回给被测试类:

@Slf4jpublic class TxtIterator implements Iterator<Object[]> {
    /**     *  数据文件     */    File txtFile;    BufferedReader bs;    String currentLine;
    public TxtIterator(File txtFile) throws IOException {        super();        this.txtFile = txtFile;        try {            bs = new BufferedReader(new FileReader(txtFile));        } catch (FileNotFoundException e) {            log.error("文件找不到");            e.printStackTrace();        }        currentLine = bs.readLine();    }
    @Override    public boolean hasNext() {        if (currentLine != null) {            return true;        } else {            return false;        }    }
    @Override    public String[] next() {        String returnLine = currentLine;        try {            currentLine = bs.readLine();        } catch (IOException e) {            e.printStackTrace();        }        return returnLine.split(",");    }
    @Override    public void remove() {        throw new UnsupportedOperationException("remove");    }}

@DataProvider 函数调用:

    String filePath; // 文件名
    @Parameters({"filePath"})    @BeforeClass()    public void beforeClass(String filePath) {        log.info("文件路径:[{}]",filePath);        this.filePath =  System.getProperty("user.dir") + "\\" + filePath;;    }

    @DataProvider(name = "iterator")    public Iterator<Object[]> iteratorDataProvider() throws IOException {        log.info("文件路径:[{}]",filePath);        return new TxtIterator(new File(filePath));    }

@Test 测试运行函数:

    @Test(dataProvider = "iterator" ,description = "测试延迟提供数据")    public void testcase2(String username,String password) throws InterruptedException {       log.info(" username = [{}] ,password = [{}]" ,username,password );
       // 休眠2秒       Thread.sleep(2000);    }

整体测试类代码:

@SpringBootTest@Slf4jpublic class DataProviderTest extends AbstractTestNGSpringContextTests {
    String filePath; // 文件名
    @Parameters({"filePath"})    @BeforeClass()    public void beforeClass(String filePath) {        log.info("文件路径:[{}]",filePath);        this.filePath =  System.getProperty("user.dir") + "\\" + filePath;;    }

    @DataProvider(name = "iterator")    public Iterator<Object[]> iteratorDataProvider() throws IOException {        log.info("文件路径:[{}]",filePath);        return new TxtIterator(new File(filePath));    }
    @Test(dataProvider = "iterator" ,description = "测试延迟提供数据")    public void testcase2(String username,String password) throws InterruptedException {       log.info(" username = [{}] ,password = [{}]" ,username,password );
       // 休眠2秒       Thread.sleep(2000);    }
}

testng.xml

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"><suite name="Suite">    <parameter name="filePath" value="data.txt"/> <!--文件名-->    <test name="DataProviderTest">        <classes>            <class name="com.zuozewei.springboottextdatadrivendemo.TestCase.DataProviderTest" />        </classes>    </test></suite>

数据文件 data.txt

Liming,12HanMeimei,13Lily,11Lucy,12

运行结果:

小结

  • 运行 testng.xml,找到对应的测试类,执行前需要初始化 filePath 参数,于是从 testng.xml 文件中把参数的值取出来,传给了测试类中的 filePath 变量。
  • 开始执行测试,发现该测试方法需要一个 DataProvider,于是在本类中找到了 iteratorDataProvider() 方法,执行该方法,构造出 Iterator 对象,传递给测试方法。
  • Iterator 对象使用了 filePath 值构造出一个 BufferedReader 对象,每当测试方法需要一条数据时就由 BufferedReader 读一条数据出来,再拆分成数组,返回给测试方法调用。

这样就实现了延迟提供的数据驱动

示例代码:

https://github.com/7DGroup/Java-API-Test-Examples/tree/master/springboot-text-data-driven-demo

本文分享自微信公众号 - 7DGroup(Zee_7DGroup),作者:左泽位

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-11-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 自动化工具之Appium工具简单介绍

    自动化,性能测试,接口测试,开发平台等工作,到底测试的价值在哪里,其实价值来源不断充实与为大众服务,今天简单介绍ui小工具appium攻击。

    高楼Zee
  • 性能工具之Jmeter常用定时器

    作用:通过ThreadDelay设定每个线程请求之前的等待时间(单位为毫秒)。

    高楼Zee
  • 性能工具之Jmeter小白入门系列之四

    青,取之于蓝,而青于蓝;冰,水为之,而寒于水。木直中绳,輮以为轮,其曲中规。虽有槁暴,不复挺者,輮使之然也。故木受绳则直,金就砺则利,君子博学而日...

    高楼Zee
  • RabbitMQ入门-Routing直连模式

    Hello World模式,告诉我们如何一对一发送和接收消息; Work模式,告诉我们如何多管齐下高效的消费消息; Publish/Subscribe模式,告...

    JackieZheng
  • C++类成员指针

    成员指针是C++引入的一种新机制,它的申明方式和使用方式都与一般的指针有所不同。成员指针分为成员函数指针和成员数据指针。

    Dabelv
  • 【8】进大厂必须掌握的面试题-Java面试-异常和线程

    错误是在运行时发生的不可恢复的情况。如OutOfMemory错误。这些JVM错误无法在运行时修复。尽管可以在catch块中捕获错误,但是应用程序的执行将停止并且...

    Java架构师必看
  • 青铜抑或王者,不同的圈层不同的人生

    最近很多事件让我们对身处的社会基础有了更多观察的角度:比如拼多多,让我们知道国人之间差距之大,底层人数庞大;比如滴滴,让我们了解长大了的“留守儿童”的问题……

    金融民工小曾
  • 抖音开放平台用户授权获取用户的粉丝统计和短视频数据

    最近有朋友问起我有没有做过抖音开放平台,让我有了些思考,其实之前做过的。虽然抖音APP很火,但是毕竟不像微信开放平台那样,已沉淀多年,基本上每个API只要肯用心...

    悟空码字
  • 整合Kafka到Spark Streaming——代码示例和挑战

    作者Michael G. Noll是瑞士的一位工程师和研究员,效力于Verisign,是Verisign实验室的大规模数据分析基础设施(基础Hadoop)的技术...

    CSDN技术头条
  • java-逆波兰算法

    IT故事会

扫码关注云+社区

领取腾讯云代金券