5.2 ConstraintStream约束测试

796 阅读3分钟

这是我参与更文挑战的第20天,活动详情查看: 更文挑战

前情提示

上一篇文章我们学习了,Drools规则约束的测试方法。今天我们来学习ConstraintStream方式的规则测试。话不多说,我们来开始学习。

内容

ConstraintStream包括Constraint Verifier单元测试线束。要使用它,首先添加一个optaplanner-test.jar

测试单个约束条件

我们以N皇后为例子:

    protected Constraint horizontalConflict(ConstraintFactory factory) {
        return factory
                .fromUniquePair(Queen.class, equal(Queen::getRowIndex))
                .penalize("Horizontal conflict", SimpleScore.ONE);
    }

下面的例子使用ConstraintVerifier API来为前面的约束流创建一个简单的单元测试:

    private ConstraintVerifier<NQueensConstraintProvider, NQueens> constraintVerifier
            = ConstraintVerifier.build(new NQueensConstraintProvider(), NQueens.class, Queen.class);

    @Test
    public void horizontalConflictWithTwoQueens() {
        Row row1 = new Row(0);
        Column column1 = new Column(0);
        Column column2 = new Column(1);
        Queen queen1 = new Queen(0, row1, column1);
        Queen queen2 = new Queen(1, row1, column2);
        constraintVerifier.verifyThat(NQueensConstraintProvider::horizontalConflict)
                .given(queen1, queen2)
                .penalizesBy(1);
    }

这个测试确保当同一行有两个皇后时,horizontalConflict约束会分配一个1的惩罚。下面一行创建了一个共享的ConstraintVerifier实例,并用NQueensConstraintProvider初始化了该实例。

    private ConstraintVerifier<NQueensConstraintProvider, NQueens> constraintVerifier
            = ConstraintVerifier.build(new NQueensConstraintProvider(), NQueens.class, Queen.class);

@Test注解表示该方法是你选择的测试框架中的单元测试。Constraint Verifier与许多测试框架一起工作,包括JUnit和AssertJ。

测试的第一部分是准备测试数据。在这种情况下,测试数据包括皇后计划实体的两个实例和它们的依赖关系(row、column)。

        Row row1 = new Row(0);
        Column column1 = new Column(0);
        Column column2 = new Column(1);
        Queen queen1 = new Queen(0, row1, column1);
        Queen queen2 = new Queen(1, row1, column2);

再往下看,下面的代码是测试约束条件的:

    constraintVerifier.verifyThat(NQueensConstraintProvider::horizontalConflict)
            .given(queen1, queen2)
            .penalizesBy(1);

verifyThat()调用是用来指定被测试的NQueensConstraintProvider类的一个方法。这个方法必须对测试类可见,Java编译器将强制执行。

given()调用是用来列举约束流将操作的所有事实。在本例中,given()调用采用先前创建的queen1queen2实例。另外,你也可以在这里使用givenSolution()方法,提供一个规划的解决方案。

最后,policizesBy()调用完成测试,确保水平冲突约束,给定一个Queen,结果是1的惩罚。这个数字是约束流中定义的匹配权重乘以匹配数的score

另外,你可以使用rewardsWith()调用来检查奖励而不是惩罚。这里使用的方法取决于相关的约束流是以惩罚还是以奖励构建块来终止的。

测试所有约束条件

除了测试单个约束之外,还可以测试整个ConstraintProvider实例。考虑一下下面的测试:

    @Test
    public void givenFactsMultipleConstraints() {
        Queen queen1 = new Queen(0, row1, column1);
        Queen queen2 = new Queen(1, row2, column2);
        Queen queen3 = new Queen(2, row3, column3);
        constraintVerifier.verifyThat()
                .given(queen1, queen2, queen3)
                .scores(SimpleScore.of(-3));
    }

与前面的例子只有两个明显的不同。

首先,这里的verifyThat()调用不需要参数,表示整个ConstraintProvider实例正在被测试。

第二,这里使用了 scores() 方法,而不是 penalizesBy() rewardsWith() 调用。该方法在给定的事实上运行ConstraintProvider,并返回由给定事实产生的所有约束匹配的 **"分数 "**之和。

使用这个方法,可以确保约束条件提供者不会遗漏任何约束条件,并且随着你的代码库的发展,评分函数保持一致。

结果查看

不匹配结果:


java.lang.AssertionError: Broken expectation.
        Constraint: org.optaplanner.examples.nqueens.domain/Horizontal conflict
  Expected penalty: 1 (class java.lang.Integer)
    Actual penalty: 0 (class java.lang.Integer)

  Explanation of score (0):
    Constraint match totals:
        0: constraint (Horizontal conflict) has 0 matches:
    Indictments:


	at org.optaplanner.test.impl.score.stream.DefaultSingleConstraintAssertion.assertImpact(DefaultSingleConstraintAssertion.java:112)
	at org.optaplanner.test.impl.score.stream.DefaultSingleConstraintAssertion.penalizesBy(DefaultSingleConstraintAssertion.java:60)
	at org.optaplanner.t

正常的结果跟上一章的一样。

总结

通过这个例子,我们学习了OptaPlanner如何测试ConstraintStream的约束规则,这对我们来说非常的重要,因为你不会期望着在生产环境测试,或者本地需要一套完整的求解数据才能开始测试。

结束语

下一篇章我们来学习如何查看约束评分的例子。

创作不易,禁止未授权的转载。如果我的文章对您有帮助,就请点赞/收藏/关注鼓励支持一下吧💕💕💕💕💕💕