如何测试生成的字符串顺序无关紧要?

时间:2013-01-03 15:39:31

标签: java string unit-testing

如何对结束顺序相当灵活的生成字符串进行单元化。让我们说我正在尝试测试一些代码,这些代码打印出来自键值对的生成的SQL。但是,许多碎片的确切顺序并不重要。

例如

SELECT
    *
FROM
    Cats
WHERE
    fur = 'fluffy'
OR
    colour = 'white'

在功能上与

相同
SELECT
    *
FROM
    Cats
WHERE
    colour = 'white'
OR
    fur = 'fluffy'

条件子句的生成顺序无关紧要,但是它们遵循where子句并不重要。此外,很难预测,因为循环entrySet()的{​​{1}}时对的排序是不可预测的。对密钥进行排序可以解决这个问题,但是对于没有(或负面)业务价值会引入运行时惩罚。

如何在不过度指定订单的情况下对这些字符串的生成进行单元测试?

我考虑使用正则表达式但是 *我想不出如何写一个说:

一个正则表达式是我的想法,但我可以想到一个正则表达式,例如“SELECT * FROM Cats WHERE”,然后是{“fur ='fluffy',color ='white'}后跟”或“HashMap color ='white'} ...而不是上次使用的那个。

NB:我实际上并没有使用SQL,这只是为了更容易解决问题。

4 个答案:

答案 0 :(得分:4)

我看到了几个不同的选择:

如果您可以适度运行时惩罚,LinkedHashMap会保持插入顺序。

如果你想在不改变实现的情况下完全解决这个问题,在你的例子中,我不明白为什么你要做的事情要比检查每个片段出现在代码中更复杂,并且它们出现在{{ 1}}。伪代码:

WHERE

但我们可以做得更简单。因为你实际上 没有用大量参数来测试它 - 对于大多数实现来说,只有三个重要的数量,“无,一个或多个” - 实际上它是可行的所有可能的值:

Map<String, String> parametersAndValues = { "fur": "fluffy", "colour", "white" };
String generatedSql = generateSql(parametersToValues);
int whereIndex = generatedSql.indexOf("WHERE");
for (String key, value : parametersAndValues) {
    String fragment = String.format("%s = '%s'", key, value);
    assertThat(generatedSql, containsString(fragment));
    assertThat(whereIndex, is(lessThan(generatedSql.indexOf(fragment))));
}

编辑:避免手工编写所有可能的变体(如果您有超过两个或三个项目,则会变得相当繁琐,因为有 n!组合方式< em> n 项目),您可以查看the algorithm for generating all possible permutations of a sequence并执行以下操作:

String variation1 = "SELECT ... WHERE fur = 'fluffy' OR colour = 'white'";
String variation2 = "SELECT ... WHERE colour = 'white' OR fur = 'fluffy'";
assertThat(generatedSql, is(anyOf(variation1, variation2)));

答案 1 :(得分:1)

好吧,一个选项是以某种方式解析SQL,提取字段列表并检查一切正常,忽略字段的顺序。然而,这将是相当丑陋的:如果做得对,你必须实现一个完整的SQL解析器(显然是矫枉过正的),如果你使用正则表达式或类似的方法快速和肮脏,你冒险测试将破坏未成年人更改生成的SQL。

相反,我建议使用单元和集成测试的组合:

  • 进行单元测试,测试提供构建SQL字段列表的代码。即,有一个方法Map getRestrictions(),您可以轻松地进行单元测试。
  • 对整个SQL生成进行集成测试,该测试针对真实数据库运行(可能是某些嵌入式数据库,如H2数据库,您可以从测试开始)。

这样,您可以对提供给SQL的实际值进行单元测试,并进行集成测试,以确保您真正创建了正确的SQL。

注意:我认为这是“集成代码”的一个例子,它不能用于单元测试。问题是代码本身不能产生真实的,可测试的结果。相反,它的目的是与数据库(通过发送SQL)进行交互,从而产生结果。换句话说,代码做正确的事情,如果它产生一些特定的SQL字符串,但如果它驱动数据库做正确的事情。因此,只能使用数据库对此代码进行有意义的测试,即在集成测试中。

答案 2 :(得分:0)

首先,使用LinkedHashMap代替常规HashMap。它不应该引入任何明显的性能下降。它不是排序,而是保留插入顺序。

其次,以很好理解的方式将对插入到地图中。也许您从表中获取数据,添加排序索引是不可接受的。但也许数据库可以按主键或其他东西排序。

结合起来,这两个变化应该会给你带来可预测的结果。

或者,使用比字符串等于更聪明的东西来比较实际与预期。也许是一个正则表达式来清除已注入实际SQL查询的所有对?

答案 3 :(得分:-2)

到目前为止,我遇到的最好的方法是在测试期间使用一些库(suck as PowerMockito)将HashMap替换为SortedMap,如TreeMap。对于测试,这种方式将被修复。但是,这仅在映射不是使用生成字符串的相同代码构建时才有效。