专栏首页搜狗测试PDF文档的自动化测试

PDF文档的自动化测试

1、背景

小编所在的项目一直以来存在一个效率较低的问题:按照产品流程,我们会在某一环节为用户提供合同,并结合用户的个人信息对合同进行填充,生成pdf,进行签章后提供给用户。 针对这个合同的测试,我们不仅需要结合用户个人信息,比对合同填充的正确性,同时也要保证用户每次生成合同的内容是正确且一致的。而针对合同的测试手段,最早开始是通过人工比对合同填充内容与数据库数据的方式进行的。但随着项目的发展,用户协议/合同数量增多、第三方接入新合同引入、代码优化重构合同相关用例必须要在多产品线进行回归,导致小编所在团队的工作量陡增。 虽然从流程上,在新合同引入时我们可以将合同确认的工作交给上游产品或商务同学,但人为地比对仍无法保证内容的正确性,且工作内容上也带来了较多重复。

2、自动化框架的搭建思路

2.1、需求分析: 找到了问题,现在我们简单分析一下需求:

  • 场景一:第三方接入拿到新合同模板,测试合同内容填充数据正确性(填充数据与数据库数据一致性)
  • 场景二:合同/签章部分代码改动,原有多产品线的合同/签章需要回归测试,验证与基线代码下的合同内容一致

2.2、设计思路: 场景一:最直接的方案是引入外部jar包,如PDFBox( https://pdfbox.apache.org/index.html)。PDFBox是Apache下的一个开源项目,我们可以通过 PDFBox读取、创建PDF文档,加密/解密PDF文档,从PDF和XFDF格式中导入或导出表单数据 等,实现代码如下:

private static String readPDF(File pdf) throws InvalidPasswordException,    IOException {       try (PDDocument document = PDDocument.load(pdf)) {           document.getClass();           if (!document.isEncrypted()) {
               PDFTextStripperByArea stripper = new PDFTextStripperByArea();               stripper.setSortByPosition(true);               PDFTextStripper tStripper = new PDFTextStripper();               String pdfFileInText = tStripper.getText(document);               String lines[] = pdfFileInText.split("\\r?\\n");               List<String> pdfLines = new ArrayList<>();               StringBuilder sb = new StringBuilder();               for (String line : lines) {                   System.out.println(line);                   pdfLines.add(line);                   sb.append(line + "\n");               }               return sb.toString();           }       }       return null;}

问题:经测试使用,PDFBox提取出来的仅是文字流,而不是带有格式、顺序、标题的文档,经过PDFBox输出的字符串,我们仍需要全篇进行解析,处理并提取其中的关键字与填充信息,这样做很费劲而且不优雅。 另外一种实现思路是将文档转为有标记的文档,比如xml、html,这样的话在完成转化后我们就可以通过标签快速找到想要的元素并进行后续的操作。经调研,转化PDF文档的外部库很多,这里我们选择itextpdf。PDF和HTML的互相转化方法如下:

 public static String generatePDFFromHTML(String filename, String outputPa   th) throws ParserConfigurationException, IOException, DocumentException {           Document document = new Document();           PdfWriter writer = PdfWriter.getInstance(document, new FileOutput   Stream(outputPath));           document.open();           XMLWorkerHelper.getInstance().parseXHtml(writer, document, new Fi   leInputStream(filename));           document.close();           return outputPath;       }       public static String generateHTMLFromPDF(String filename, String outp   utPath) throws ParserConfigurationException, IOException {           PDDocument pdf = PDDocument.load(new File(filename));           PDFDomTree parser = new PDFDomTree();           Writer output = new PrintWriter(outputPath, "utf-8");           parser.writeText(pdf, output);           output.close();           if (pdf != null) {           pdf.close();           }           return outputPath; }

在完成了HTML的转化后,我们需要做的就是从HTML解析想要的元素了。小编以前写爬虫时最常用的Java HTML解析器就是Jsoup(http://www.open-open.com/jsoup/)。Jsoup不仅可以解析HTML文件、同时也直接通过HTTP、HTTPS去爬取网页源码进行解析,很方便,实现如下:

import org.jsoup.Jsoup;   import org.jsoup.nodes.Document;   import org.jsoup.nodes.Element;   public class JsoupTest {      public static void main(String[] args) {         String html = "<html><head><title>Sample Title</title></head>"            + "<body>"            + "<p>Sample Content</p>"            + "<div id='sampleDiv'><a href='www.google.com'>Google</a>"            + "<h3><a>Sample</a><h3>"            +"</div>"            + "<div id='imageDiv' class='header'><img name='google' src='goo   gle.png' />"            + "<img name='yahoo' src='yahoo.jpg' />"            +"</div>"            +"</body></html>";            Document document = Jsoup.parse(html);            //通过标签提取文字            Element link = document.select("a").first(); System.out.println("Text: " + link.text());            // 查找.png结尾的图片的名字            Elements pngs = document.select("img[src$=.png]"); for (Element png : pngs) {            System.out.println("Name: " + png.attr("name"));         }            // 查找H3元素之后的元素            Elements sampleLinks = document.select("h3 > a"); for (Element link : sampleLinks) {            System.out.println("Text: " + link.text());         }  } }

在前两步完成后,后面要做的就是从数据库拿数据进行比对,这里就不再赘述了

场景二:此场景的整体思路就是拿到此基线下的各合同PDF,然后拿新生成的合同进行比对,比对内容包括格式、文案、图片、签章坐标系等。如果复用上面的思路,那么实现原理是提取合同中的所有元素进行比较。这里存在的一个问题是一整个流程下来可能存在十数个合同,我们需要针对每个合同进行一一解析;另外此方法也无法针对位置一类的校验点进行检查。 经小编的再次调研,网上有很多的文档比对解决方案,其中applitools(https://applitools.com/)提供了CLI的解决方案,我们只需注册一个免费账号,获取到apikey,执行命令即可。

java -jar ImageTester.jar -k $APPLITOOLS_API_KEY -f <PATH>/pdf_directory/

那么问题来了,如何把此步骤加到整个自动化的流程中呢?

步骤一:移动下载的PDF至指定的目录

File downloadedPDF = new File("C:\\Users\\Tests\\Downloads\\" + contract+   ".pdf");   String destination = "C:\\Users\\resources\\contracts\\" + contract+ ".pd   f";   FileUtils.moveFile(downloadedPDF, destination);    public static boolean moveFile(File file, String destination){           File existingFile = new File(destination);           if(existingFile.exists()){               existingFile.delete();           }           return file.renameTo(new File(destination));       }

步骤二:执行上面的jar包,小编这里写了个各处可用的Util方法

public static boolean validatePDF(String filepath) throws IOException, In   terruptedException {           String command = String.format(                   "java -jar C:\\Users\\resources\\contracts\\ImageTester.j   ar -k %s -f %s",                   System.getProperty("applitools.api.key"),                   filepath);           Process process = Runtime.getRuntime().exec(command);           process.waitFor();           String stream = IOUtils.toString(process.getInputStream(), "UTF-   8");           System.out.println(stream);           if(stream != null && stream.contains("Mismatch")){               return false;           }           return true;}

步骤三::用断言进行比对测试

Assert.assertTrue("Error validating PDF", validatePDF(destination));

完事收工。

3、总结

以上就是小编解决此项目中问题的全部心路历程与思路。总结来说,在测试中做自动化的核心意义 在于解决重复的、低生产力的人工工作,让机器赋能工程师们追求更快更全面与更深入的测试。

本文分享自微信公众号 - 搜狗测试(SogouQA),作者:Boris

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

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

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 又掉坑里了——手机智能分辨率

    IT行业的迅猛发展逼迫着我们不断的适配各种新颖的功能,比如本文要介绍的功能——手机智能分辨率。

    用户5521279
  • 智能硬件sdk测试初探

    近期公司推出了某款智能录音笔,需要对录音笔笔端一些应用的sdk进行测试。因为之前对关于sdk测试的了解并不是很多,所以在本次测试中边测试、边了解、边学习,总结...

    用户5521279
  • 控制你的数据——Python mock的基本使用

    Mock即模拟的意思。在Python中,提供了基于单元测试的mock模块,它的主要作用是使用mock对象替代掉指定的Python对象,以达到模拟对象功能的行为...

    用户5521279
  • Kafka 0.8 Producer (0.9以前版本适用)

    Kafka旧版本producer由scala编写,0.9以后已经废除,但是很多公司还在使用0.9以前的版本,所以总结如下: 要注意包Producer是 kaf...

    实时计算
  • Spring Boot(三):RestTemplate提交表单数据的三种方法

    在REST接口的设计中,利用RestTemplate进行接口测试是种常见的方法,但在使用过程中,由于其方法参数众多,很多同学又混淆了表单提交与Payload提交...

    业余草
  • 如何在Ubuntu 14.04上使用Shipyard部署Wordpress

    Shipyard是Docker服务器的管理工具。Docker是用于集装箱化的尖端软件。Shipyard允许您查看每个服务器正在运行的容器,以便启动或停止现有容器...

    小铁匠米兰的v
  • wpf 单例

    本文告诉大家如何做一个 wpf 单例程序。单例就是用户只能运行这个程序一次,也就是内存只有存在这个程序一个。

    林德熙
  • JS实现网站内容的禁止复制和粘贴、另存为

    2: 在<body>中加入以下代码: <body oncontextmenu="return false" onselectstart="return fals...

    庞小明
  • MySql 缓存查询原理与缓存监控 和 索引监控

    mysql执行查询语句之前,把查询语句同查询缓存中的语句进行比较,且是按字节比较,仅完全一致才被认为相同。如下,这两条语句被视为不同的查询

    授客
  • 【 文智背后的奥秘 】系列篇 : 分布式爬虫之 WebKit

    WebKit 是由 Apple 公司开发的开源浏览器内核,WebKit 主要分为三个模块:WebCore、JavaScriptCore、平台应用相关Port。相...

    文智

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动