扩展 Rational Functional Tester 以测试 PDF 文件

Comments
免费下载:IBM® Rational® Functional Tester 试用版  |   IBM® Rational® 测试人员资源工具包
获取免费的 Rational 软件工具包系列,下载更多的 Rational 软件试用版

开始

在这一部分中,您将会学到怎样使用 IBM® Rational® Functional Tester 来创建一个通用的确认点(verification point,VP),它会比较两个 PDF 文件的文本。代码清单 1 显示了怎样将 PDF 文件放到 pdfFiles 的文件夹中。Expected.pdf 文件就是您预期的结果,而 Actual.pdf 就是获得的文件。脚本会将 Expected.pdfActual.pdf 进行比较。

首先,创建一个名为 PDFBox 的包。您可以调用 PDFBox 方法来从 Rational Functional Tester 脚本处访问 PDF 文件的文本,以载入 PDF 文件并从文件中获取数据。

代码清单 1 显示了如何使用 PDF VP 来比较两个 PDF 文件的脚本。

清单 1. 比较 PDF 文件的脚本
package testScripts;
import resources.testScripts.pdfboxExampleHelper;

public class pdfboxExample extends pdfboxExampleHelper
{
  public void testMain(Object[] args)
    {
      String expected = "pdfFiles\\Expected PDF.pdf";
      String actual = "pdfFiles\\Actual PDF.pdf";

      vpPdfText("PDF",expected,actual).performTest();
      vpPdfText("PDF Date Mask",expected,actual,
        MASK_DATE, MASK_DATE_NAME).performTest();
    }
}

第一个确认点是一个直接的比较。第二个确认点使用一个蒙版来忽略时间。

配置 Rational Functional Tester Java 项目来使用 PDFBox

为了在新的 Rational Functional Tester Java™ 项目中创建 PDFBox,您要向 Rational Functional Tester Java 项目中 Java 构建路径的 PDFBox JAR(Java Archive)文件添加一个引用(PDFBox-0.7.3.jarFontBox-0.1.0-dev.jar)。您可以从 Apache Software Foundation 中下载它们。对于该网站的连接,您可以在本文的结尾查看 参考资料 部分。

配置 Rational Functional Tester Java 项目以使用 google-diff-match-patch 库

Google 包 google-diff-match-patch 库作为源代码,您可以从 Diff,Match 和 Patch 库处下载它们。如果您想要得到该网站的链接,那么您可以查看本文结尾处的 参考资料 部分。为了使用它,您需要将源代码导入到 Rational Functional Tester Java 项目,或者创建一个 JAR 文件并引用 JAR 文件。这个过程中,提供了一个名为 google-diff-match-patch.jar 的 JAR 文件,该文件位于您所导入的转移文件之中。它位于名为 jars 的文件夹中。您可以通过将其添加至项目的 Java 构建路径来使用它。按照下面的方法来进行操作。

理解代码

方案由以下的部分组成:

  • PDFTextVerificationPoint:它代表了 PDF 文件文本的确认。除了使用 PDFBox 类来从 PDF 文件导出文本,该类还实现了 IFtVerificationPoint 界面。
  • IFtVerificationPoint:它提供了诸如 performTestcompare 之类的方法,它使用 Diff Match Patch 类来智能地比较预期 PDF 文件和实际 PDF 文件的内容。考虑到两个 PDF 文件之间可能存在一些文本上的差异,例如日期,您可以提供一个蒙版来忽略。您要使用正则表达式来实现该蒙版。performTestcompare 方法会忽略匹配正则表达式的文本。
  • PdfBoxHelperSuperClass: 该 Helper 超类拥有的方法和变量,可以使在文本脚本中创建 PDF 文本确认点变得容易。

图 1 显示了一个范例的 PDFBox 类图。

图 1.PDFBoxExample 类图
PDF BoxExample 类图
PDF BoxExample 类图

图 1 的大图

这一章节的剩余部分描述了 PDF 确认点是如何发挥作用的。PdfTextVerificationPoint 类会首先得到评审,然后就是 PdfBoxHelperSuperClass

导入声明

您需要导入 PdfTextVerificationPoint 类中的一些库,来轻松从 PDFBox、google-diff-match-patch 库、IftVerificationPoint,以及一些设施类轻松访问方法,如代码清单 2 所示。

清单 2. 输入声明
import java.util.Calendar;
import java.util.LinkedList;
import name.fraser.neil.plaintext.diff_match_patch;
import name.fraser.neil.plaintext.diff_match_patch.Diff;
import org.pdfbox.pdmodel.PDDocument;
import org.pdfbox.util.PDFTextStripper;
import com.rational.test.ft.script.RationalTestScript;
import com.rational.test.ft.vp.IFtVerificationPoint;

数据成员与构造器

PdfTextVerificationPoint 类会追踪大量的数据项。它拥有一个确认点,预期的文本,以及实际的数据。它还需要追踪预期 PDF 文件及实际 PDF 文件的位置。最终,PdfTextVerificationPoint 就拥有一个蒙版和一个蒙版名了。

一个蒙版就是个正则表达式,它代表了一次比较期间应该被忽略的文本。蒙版名使用蒙版所匹配的文本。PdfTextVerificationPoint 类拥有两个构造器,如代码清单 3 所示。一个构造器会传递蒙版和蒙版名,其他的则不会。两个构造器都会使用传递的参数来升级成员,然后调用一个称为 extractText 的方法,来从预期的 PDF 文件和实际的 PDF 文件处获得文本。

清单 3. 数据成员与构造器
private String vpName;
private String expectedFilename;
private String actualFilename;
private String expectedText;
private String actualText;
private String mask;
private String maskName;
         
public PdfTextVerificationPoint(String vpName,
    String expectedFilename,
    String actualFilename) {
  super();
  this.vpName = vpName;
  this.expectedFilename = expectedFilename;
  this.actualFilename = actualFilename;
this.mask = "";
  this.maskName = "";
  this.expectedText = "";
  this.actualText = "";

  //Get the text from the pdf files.
  extractText();
}

public PdfTextVerificationPoint(String vpName,
    String expectedFilename,
    String actualFilename,
    String mask,
    String maskName) {
  super();
  this.vpName = vpName;
  this.expectedFilename = expectedFilename;
  this.actualFilename = actualFilename;
  this.mask = mask;
  this.maskName = maskName;
  this.expectedText = "";
  this.actualText = "";
  
  //Get the text from the pdf files.
  extractText();
}

从 PDF 中获取文本

PdfTextVerificationPoint 类需要从预期的和实际的 PDF 文件中获取文本。您可以使用来自 PDFBox 库的类来轻松编写代码。PDDocument 类代表了 PDF 文件,而 PDFTextStripper 知道怎样从 PDF 文件中获取文本。在您获取文本之后,您就使用蒙版名来替换匹配蒙版文本的所有实例(如果需要蒙版的话),如代码清单 4 所示。

清单 4. 获取文本
/*
  * Description: Extracts the text from the expected and actual pdf
  * files. If a mask is defined, all occurrences of the substring
  * the mask defines are replaced with the text maskName.
*/
private void extractText()
{
try
  {
    //Load the PDF files
    PDDocument expectedDoc = PDDocument.load(expectedFilename);
    PDDocument actualDoc = PDDocument.load(actualFilename);
        
    //Extract the text from the PDF file.
    PDFTextStripper stripper = new PDFTextStripper();
    expectedText = stripper.getText(expectedDoc);
    actualText = stripper.getText(actualDoc);
         
    //Use the mask, if it is defined. Replace all substrings,
    //which match the regular expression, mask with maskName
    if(mask != "")
    {
      expectedText = expectedText.replaceAll(mask, maskName);
      actualText = actualText.replaceAll(mask, maskName);
}

    //Close the PDF files.
    expectedDoc.close();
    actualDoc.close();
    }
  catch (Exception e)
  {
    RationalTestScript.logException(e);
    RationalTestScript.stop();
  }
}

执行测试

PdfTextVerification 点现在拥有了测试预期 PDF 文件和实际 PDF 文件所需要的一切条件。将大量的各种复杂程度的文本进行比较,并不是一项简单的任务。幸运的是,google-diff-match-patch 库可以为您完成这项工作。只要有一小部分的代码,如代码和 5 所示,您就能够获取一个列表,该列表代表了两块文本之间的差异。而且,google-diff-match-patch 库可以以一种“完美的” HTML 格式来输出结果,以图形化的方式来显示差异。

清单 5. 执行测试
public boolean performTest(boolean compareTrueEqualsPass) {

  /*
   * Get a linked list representing the difference between
   * the text. Each node represents text which is the
   * same (EQUAL), text which was added to the actual text
   * (INSERT), or text which was deleted from expected text
   * (DELETE).
   */
  diff_match_patch dmp = new diff_match_patch();
  LinkedList<Diff> diffList =
    dmp.diff_main(expectedText,actualText,true);
  dmp.diff_cleanupSemantic(diffList);
        
  //Perform the test and log the results.
  if(expectedText.equals(actualText) == compareTrueEqualsPass) {
    RationalTestScript.logTestResult(
      "Verification point [" + vpName + "] passed.",
      true,
      dmp.diff_prettyHtml(diffList));
    return true;
  }
  else
  {
    RationalTestScript.logTestResult(
      "Verification point [" + vpName + "] failed.",
      false,
      dmp.diff_prettyHtml(diffList));
    return false;
  }
}

考虑一下账户变量 PDF 生成时间

让测试下的程序花不同的时间来生成 PDF 文件,成为可能的了。IftVerificationPoint 界面包含了 performTestcompare 方法的实例,这一点将会被考虑。调用者会提供最大测试时间(按秒计算)以及重复尝试的延迟(也以秒计算)。然后这项方法会在延迟定义的间隔时间内进行重复,直到时间用完,或者已经过了确认点为止,如代码清单 6 所示。

清单 6. 比较和执行测试
public boolean compare(double delayBetweenRetries,
    double maximumTestTime) {
  //Get the end time for the test.
  long endTime = (long) (Calendar.getInstance().getTimeInMillis()
    + (maximumTestTime * 1000));

  //perform the test until time runs out or the test passes.
  boolean result = compare();
  long nextTime = (long) (Calendar.getInstance().getTimeInMillis()
    + (delayBetweenRetries * 1000));

  while((nextTime < endTime) && result == false)
  {
    RationalTestScript.sleep(delayBetweenRetries);
    extractText();
    result = compare();
    nextTime = (long) (Calendar.getInstance().getTimeInMillis()
      + (delayBetweenRetries * 1000));
  }
  return result;
}

public boolean performTest(double delayBetweenRetries,
    double maximumTestTime, boolean compareTrueEqualsPass) {
  //Get the end time for the test.
  long endTime = (long) (Calendar.getInstance().getTimeInMillis()
    + (maximumTestTime * 1000));

  //perform the test until time runs out or the test passes.
  boolean result = (compare() == compareTrueEqualsPass);
  long nextTime = (long) (Calendar.getInstance().getTimeInMillis()
    + (delayBetweenRetries * 1000));

  while((nextTime < endTime) && result == false)
  {
    RationalTestScript.sleep(delayBetweenRetries);
    extractText();
    result = (compare() == compareTrueEqualsPass);
    nextTime = (long) (Calendar.getInstance().getTimeInMillis()
      + (delayBetweenRetries * 1000));
  }
  return performTest(compareTrueEqualsPass);
}

完成剩余的方法

还有一些 get 方法,一些 performTest 方法的实例,以及一些 compare 方法的实例,如代码清单 7 所示。

清单 7. 琐碎的方法
public boolean compare() {
  return expectedText.equals(actualText);
}

public boolean compareAndLog() {
  return compareAndLog(true);
}

public boolean compareAndLog(boolean compareTrueEqualsPass) {

  if(expectedText.equals(actualText) == compareTrueEqualsPass) {
    RationalTestScript.logTestResult(
      "Verification point [" + vpName + "] passed.",
      true);
    return true;
  }
  else
  {
    RationalTestScript.logTestResult(
      "Verification point [" + vpName + "] failed.",
      false);
    return false;
  }
}

public Object getActualData() {
  return actualText;
}

public Object getBaselineData() {
  return null;
}

public Object getExpectedData() {
  return expectedText;
}

public String getVPName() {
  return vpName;
}

public boolean performTest() {
  return performTest(true);
}

public boolean performTest(double delayBetweenRetries,
    double maximumTestTime) {

  return performTest(delayBetweenRetries,maximumTestTime,
      true);
}

Helper 超类

本方案拥有一个称作 PDFBoxHelperSuperClass 的 helper 的超类,它包含了日期字段的一个预定义蒙版,以及返回 PDF 文本确认点的一些 helper 功能,如代码清单 8 所示。

清单 8. PDFBoxHelper 超类
package util;

import com.rational.test.ft.script.RationalTestScript;
import com.rational.test.ft.vp.IFtVerificationPoint;

/**
 * Description : This Super class contains methods which may
 * be used to compare pdf files.
 *
 * @since November 26, 2008
 */
public abstract class PdfBoxHelperSuperClass extends RationalTestScript
{
  /*
   * This regular expression matches a date time stamp in
   * the format mm/dd/yyyy hh:mm PM (or AM). It may be used
   * to mask the date from the expected and actual text of a
   * pdfTextVp.
   *
   */
  public static final String MASK_DATE = "[0-9]{2}/[0-9]{2}/[0-9]{4}";
  public static final String MASK_DATE_NAME = "***MM/DD/YYYY***";

  /*
   * Description: This method returns a verification point
   * for the text of a pdf file. It replaces any text in the
   * expected and actual files, which match the regular
   * expression defined in the mask parameter with maskName.
   * One can use the mask to remove text from the vp, which
   * would cause the vp to fail incorrectly. A date would be
   * a good example of text which could be masked.
   *
   */
  protected IFtVerificationPoint vpPdfText(String vpName,
      String expected,
      String actual,
      String mask,
      String maskName)
  {
    return new PdfTextVerificationPoint(vpName,
      expected,actual,mask, maskName);
  }
  /*
   * Description: This method returns a verification point
   * for the text of a pdf file.
   */
  protected IFtVerificationPoint vpPdfText(String vpName,
      String expected,
      String actual)
  {
    return new PdfTextVerificationPoint(vpName,expected,actual);
  }
}

您学到了什么

从这篇文章中,您学到了通过扩展 Rational Functional Tester 的功能,来创建 PDF 确认点。您现在应该知道怎样将 Rational Functional Tester 与开放源码包集成起来,该包可以访问 PDF 文件之中的信息。这可以帮助您创建确认点以测试 PDF 文件。您可以从 PDF 文件中抽取测试,执行确认,并考察不同的 PDF 文件的生成时间。


相关主题

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Rational
ArticleID=630439
ArticleTitle=扩展 Rational Functional Tester 以测试 PDF 文件
publish-date=03032011