3

我正在尝试解决以下问题:

  • 我有一个 Java 应用程序(不是我编写的),其目的是获取 2 个报告文件(只是逗号分隔的表格输出),并将文件的每一行和每一列逐个单元格地相互比较 - 基本上,用于回归测试。

  • 我想用以下功能增强它:

    • 假设我做了一个软件更改,导致 C1 列中的所有值都增加了 100%。

    • 在比较特定列“C1”时,软件当前会报告 C1 中 100% 的值发生了变化。

    • 我想要做的是能够将回归测试器配置为不简单地比较“在第 1 行中的 C1 在两个文件中是否相同”,而是将预先配置的比较规则/公式应用于该字段,例如“确保文件#2 中的 C1 值是文件#1 中 C1 值的 2 倍"。这样,我不仅可以抑制 100% 的虚假回归错误,即每行的 C1 列不匹配,而且还可以使用新软件捕获 C1 列不完全 100% 大的任何实际错误。

以前在 Perl 中编写此类功能时,解决方案非常简单 - 只需将自定义的每列比较器配置编码到存储在配置文件中的 Perl 散列中,散列键是列,散列值是用于比较 2 个值的 Perl 子例程我想要的任何复杂方式。

显然,这种方法不适用于 Java,因为我不能在 Java 中编写自定义比较器逻辑,并且在不同的运行时让 Java 不同地评估/编译/执行这些比较器。

这意味着我需要提出一些特定于领域的可解释语言,我的不同之处将解释/评估/执行。

由于我对 Java 生态系统和库不是很熟悉,所以我问:

为可配置的比较器逻辑实现此 DSL 的一个好的解决方案是什么?

我的要求是:

  • 解决方案必须“像啤酒一样免费”

  • 解决方案必须是“收缩包装”。例如,一个现有的库,我可以简单地放入我的代码中,添加配置文件,然后让它工作。

    要求我编写自己的 BNF 语法并提供我必须编写自己的解释器的通用语法解析器的东西是不可接受的。

  • 就数据处理和语法丰富而言,该解决方案必须相当灵活。例如:

    • 您至少应该能够从 DSL 中传入 - 并引用/地址 - 整行数据作为哈希

    • 它应该有相当完整的语法;至少进行基本的字符串操作(连接、子字符串,理想情况下是某种级别的正则表达式匹配和替换);基本算术,包括abs(val1 - val2) > tolerance在浮点 #s 上执行的能力;和基本的流程控制/逻辑,例如条件和理想循环。

  • 该解决方案应该相当快,并且必须是可扩展的。例如,比较 100x100 大小的文件不应该花费 10 分钟和 10-20 个自定义列。

如果重要的话,目标环境是 Java 1.6

4

3 回答 3

4

有多种动态 JVM 编程语言可以轻松集成到 Java 应用程序中,而无需付出太多努力。我认为值得研究Groovy和/或Scala

另一种可能的选择是使用XTextXTend创建您自己的 DSL 。

于 2012-08-10T20:42:35.970 回答
3

每当谈到 Java 中的动态特性时,我都会想到 Janino,这是一个迷人的运行时和内存编译器。在您的情况下,它会为您提供类似于纯 Java 的 eval(...) 的内容,请参阅:http ://docs.codehaus.org/display/JANINO/Basic#Basic-expressionevaluator

这里的要点是您的测试配置没有 DSL,但您可以使用纯 Java 语法在测试配置中编写自定义表达式。

下面提出的解决方案无法满足的唯一要求是您可以从配置文件中处理整行。该解决方案假定您编写了一个 Java 测试类,它逐个值(或更好地逐对)遍历您的测试数据,并使用您配置的表达式来比较单个值。所以动态部分是表达式,静态部分是测试数据的迭代。

但是所需的代码非常小且简单,如下所示:

配置文件(Java 属性语法,键是列名,值是测试表达式):

# custom expression for column C1
C1  = a == 2 * b           
# custom expression for column C4
C4  = a == b ^ 2           
# custom expression for column C47
C47 = Math.abs(a - b) < 1  

测试代码草图:

// read config file into Properties
Properties expressions = Properties.load(....);

// Compile expressions, this could also be done lazily
HashMap<String, ExpressionEvaluator> evals = new HashMap<String, ExpressionEvaluator>();
for (String column : expressions.stringPropertyNames()) {
    String expression = expressions.getProperty(column);
    ExpressionEvaluator eval = new ExpressionEvaluator(
        expression,                  // expression
        boolean.class,               // expressionType
        new String[] { "a", "b" },   // parameterNames
        new Class[] { int.class, int.class } // parameterTypes, depends on your data
    );
    evals.put(column, eval);
}

// Now for every value pair (a, b) check if a custom expression is defined 
// for the according column and if yes execute: 
Boolean correct = (Boolean) theEvalForTheColumn.evaluate(
    new Object[] { a, b }          // parameterValues
);
if (!correct) throw Exception("Wrong values...");

正如 Janino 页面上所说,编译表达式的性能非常好(它们是真正的 java 字节码),只有编译会减慢进程。因此,如果您有许多自定义表达式,但它应该随着值数量的增加而很好地扩展,这可能是一个问题。hth。

于 2012-08-10T22:56:43.860 回答
0

不需要嵌入式语言。将您的比较器定义为接口。

您可以使用 class.forName(name) 加载在运行时定义接口的类,其中名称可以通过命令行参数或任何其他方便的方式指定。

你的比较器类看起来像

class SpecialColumn3 implements ColumnCompare
{ boolean compare(String a,String b) {...}
}
于 2012-08-10T23:10:19.803 回答