我正在寻找一种在单元测试中比较两个MySQL查询的方法。你知道任何允许的库(所有这些断言都应该通过):
SQLAssert.assertEquals("select id, name from users", "select id, name from users")
SQLAssert.assertEquals("select id, name from users", "select `id`,`name` from `users`")
答案 0 :(得分:2)
虽然针对内存数据库运行查询并比较结果是最好的答案,但我认为不太全面且更脆弱的选项仍然有用。
在实践中,您可能会对查询的语法设置其他限制。在您的示例中,只有select语句,单个表,没有where子句,并且唯一的查询差异是反引号和空格,因此编写一个使用这些约束规范化查询的方法可能是可行的。类似的东西:
private String normalize(String str) {
return str.replaceAll(" +", " ").replaceAll("`", "");
}
然后可以比较这些规范化的字符串。这种做事方式非常脆弱(因此不是未来证据),但这并不意味着它在某些情况下无法提供价值。当然,有相当多的有效sql语句会导致这种情况中断,但是你不必处理有效sql所需的完整字符串集。您只需要处理查询使用的任何sql子集。
如果您的查询不同以使此代码不合理,则可能更容易使用像JSqlParser这样的解析器库来解析碎片,然后导航结构以进行比较。同样,您不必支持所有SQL,只需支持查询使用的任何子集。此外,测试不必测试完全逻辑等效是有用的。测试可能只是确保两个查询中提到的所有表都是相同的,无论连接和排序如何。这并不能使它们等效,但它确实可以防止某种特定的错误,并且比没有错误更有用。
这可能有用的情况的一个示例是,如果您在查询构建器上执行大量重构,并且您希望确保结束查询是等效的。在这种情况下,您不是自己测试查询,而是查询构建。
我不建议将其作为单元测试中的常规练习,但我认为它在非常特殊的情况下非常有用。
答案 1 :(得分:2)
您可以使用 JSqlParser 来解析您的查询。然后,您可以使用 JSqlParser 的所谓 Deparser 来获取SQL的版本,而无需额外的空格,制表符和换行符。从此开始,您可以使用简单的字符串相等性检查。当然你必须处理各种报价,如"或[]但它的工作原理,如示例代码所示。
这不起作用,引用发挥作用或SQL中的列或表达式的不同顺序。引用问题很容易通过扩展到表达式deparser来解决。
Statement stmt1 = CCJSqlParserUtil.parse("select id, name from users");
Statement stmt2 = CCJSqlParserUtil.parse("select id, name from users");
Statement stmt3 = CCJSqlParserUtil.parse("select `id`,`name` from `users`");
//Equality
System.out.println(stmt1.toString().equals(stmt2.toString()));
ExpressionDeParser exprDep = new ExpressionDeParser() {
@Override
public void visit(Column tableColumn) {
tableColumn.setColumnName(tableColumn.getColumnName().replace("`", ""));
super.visit(tableColumn);
}
};
SelectDeParser stmtDep = new SelectDeParser() {
@Override
public void visit(Table tableName) {
tableName.setName(tableName.getName().replace("`", ""));
super.visit(tableName);
}
};
exprDep.setBuffer(stmtDep.getBuffer());
stmtDep.setExpressionVisitor(exprDep);
((Select)stmt3).getSelectBody().accept(stmtDep);
String stmt3Txt = stmtDep.getBuffer().toString();
System.out.println(stmt1.toString().equals(stmt3Txt));
答案 2 :(得分:0)
我建议断言2个查询返回相同结果的唯一方法是实际运行它们。当然,您不想要做的是将单元测试连接到真实数据库。这有很多原因:
测试可能会影响数据库中的内容,并且您不希望将大量测试数据引入生产数据库
每个测试都应该是自包含的,并且每次运行时都以相同的方式工作,这要求数据库在每次运行开始时处于相同的已知状态。这需要为每个测试重置 - 而不是与生产(或开发环境)数据库有关。
考虑到这些限制,我建议您研究DBUnit,它是专为数据库驱动的JUnit测试而设计的。我还建议,不要使用MySQL进行单元测试,而是使用内存数据库(示例使用HSQLDB),这样就可以测试查询而不会实际存在测试数据。