使用junit进行流口水测试

Posted

技术标签:

【中文标题】使用junit进行流口水测试【英文标题】:Drools testing with junit 【发布时间】:2011-04-21 05:09:52 【问题描述】:

用junit测试drools规则的最佳实践是什么?

到目前为止,我们使用 junit 和 dbunit 来测试规则。我们有放入 hsqldb 的样本数据。我们有几个规则包,到项目结束时,很难做出好的测试输入来测试某些规则而不触发其他规则。

所以确切的问题是,我如何将 junit 中的测试限制为一个或多个特定的测试规则?

【问题讨论】:

【参考方案1】:

不要试图将规则执行限制为测试的单个规则。与 OO 类不同,单个规则并不独立于其他规则,因此以与使用单元测试测试单个类相同的方式单独测试规则是没有意义的。换句话说,要测试单个规则,请测试它与其他规则组合是否具有正确的效果。

相反,使用少量数据对您的所有规则运行测试,即规则会话中的事实数量最少,并测试结果并可能触发特定规则。结果实际上与您的想法并没有太大不同,因为一组最小的测试数据可能只会激活一两个规则。

对于样本数据,我更喜欢使用静态数据,并为每个测试定义最少的测试数据。有多种方法可以做到这一点,但在 Java 中以编程方式创建事实对象可能就足够了。

【讨论】:

是的,我知道规则执行的工作原理。这就是我们现在的做法。我的问题是这种方法很难获得足够和适当的测试数据。因为我们不限制可运行规则,所以任何其他规则都可以运行并更改最终结果。所以很难预测断言的最终结果。这就是为什么我认为测试 izolated 规则会更好的原因。 我想我想说的是,“任何其他规则都可以运行并改变最终结果”这一事实正是为什么单独测试规则意义不大的原因。【参考方案2】:

使用 DBUnit 进行单元测试实际上并不奏效。使用 DBUnit 进行集成测试。原因如下: - 单元测试应该很快。 -- DBUnit 数据库恢复很慢。轻松花 30 秒。 -- 一个真实的应用程序有许多非空列。因此,为单个特征隔离的数据仍然很容易使用数据库的一半表。 - 应该隔离单元测试。 -- 为每个测试恢复 dbunit 数据库以使它们保持隔离有缺点: --- 运行所有测试需要几个小时(特别是随着应用程序的增长),所以没有人运行它们,所以它们不断地中断,所以它们被禁用,所以没有测试,所以你的应用程序充满了错误。 --- 为每个单元测试创​​建半个数据库是大量的创建工作,大量的维护工作,很容易变得无效(关于验证哪些数据库模式不支持,请参阅 Hibernate Validator)并且通常会做坏事代表现实的工作。

改为使用 DBunit 编写集成测试: - 一个 DBunit,所有测试都相同。仅加载一次(即使您运行 500 次测试)。 -- 将每个测试包装在一个事务中,并在每次测试后回滚数据库。无论如何,大多数方法都使用所需的传播。仅当传播为 requires_new 时才将 testdata 设置为脏(如果有下一个测试,则在下一个测试中将其重置)。 - 用极端案例填充该数据库。不要添加比测试业务规则严格需要的更多常见案例,因此通常只添加 2 个常见案例(以便能够测试“一对多”)。 - 编写面向未来的测试: -- 不要测试激活规则的数量或插入事实的数量。 -- 相反,测试结果中是否存在某个插入的事实。过滤某个属性设置为 X(不同于该属性的公共值)的结果,并测试该属性设置为 X 的插入事实的数量。

【讨论】:

【参考方案3】:

我个人使用单元测试来测试孤立的规则。我认为这并没有什么太大的问题,只要您不会因为孤立的规则在起作用而误以为您的知识库正在起作用,从而产生错误的安全感。测试整个知识库更为重要。

您可以使用 AgendaFilter 和 StatelessSession 编写隔离测试

StatelessSession session = ruleBase.newStatelessSesssion();

session.setAgendaFilter( new RuleNameMatches("<regexp to your rule name here>") );

List data = new ArrayList();
... // create your test data here (probably built from some external file)

StatelessSessionResult result == session.executeWithResults( data );

// check your results here.

代码来源:http://blog.athico.com/2007/07/my-rules-dont-work-as-expected-what-can.html

【讨论】:

【参考方案4】:

我创建了一个简单的库来帮助为 Drools 编写单元测试。其中一项功能正是您所需要的:声明要用于单元测试的特定 drl 文件:

@RunWith(DroolsJUnitRunner.class)
@DroolsFiles(value = "helloworld.drl", location = "/drl/")
public class AppTest 

    @DroolsSession
    StatefulSession session;

    @Test
    public void should_set_discount() 
        Purchase purchase = new Purchase(new Customer(17));

        session.insert(purchase);
        session.fireAllRules();

        assertTrue(purchase.getTicket().hasDiscount());
    

更多详情请看博文:https://web.archive.org/web/20140612080518/http://maciejwalkowiak.pl/blog/2013/11/24/jboss-drools-unit-testing-with-junit-drools/

【讨论】:

github url 未找到并且缺少工件 pl.maciejwalkowiak:junit-drools:jar:1.0 in maven ! @YousefAlKahky 项目已归档,您可以查看github.com/maciejwalkowiak/junit-drools【参考方案5】:

单元测试是关于使用最少的代码并测试定义规范的所有可能的用例。使用集成测试,您的目标不是所有可能的用例,而是几个协同工作的单元的集成。对规则做同样的事情。按业务意义和目的划分规则。最简单的“被测单元”可以是具有单个规则或high cohension 规则集的文件,以及它工作所需的内容(如果有的话),例如常见的 dsl 定义文件和决策表。对于集成测试,您可以采用有意义的子集或系统的所有规则。

使用这种方法,您将拥有许多独立的单元测试和很少的集成测试,这些测试使用有限的通用输入数据来重现和测试常见场景。添加新规则不会影响大多数单元测试,但很少会影响集成测试,并且会反映新规则如何影响常见数据流。

考虑一下可能适合这种方法的 JUnit testing library

【讨论】:

似乎是一个不错的答案,但它在 Drools 7.5 中不起作用,因为(正如 maven repo 所说):kie.internal - Drools 和 jBPM 内部 API,在版本之间不向后兼容. :-(

以上是关于使用junit进行流口水测试的主要内容,如果未能解决你的问题,请参考以下文章

为啥伴随对象不使用 JUnit 测试作为私有字段进行测试?

使用JUNIT进行单元测试

软件测试——JUnit基础

05-使用Junit进行测试

如何使用eclipse进行junit测试

通过JUnit进行Android单元测试