是否有更简洁的方法来断言数组的每个元素都匹配一个谓词?

Is there a more concise way to assert that an array's elements each match a predicate?

我们都知道您不应该断言浮点数完全相等。 assertk 提供了一种更好的便捷方法:

assertThat(value).isCloseTo(3.0, 0.000001)

我想将其扩展到数组:

assertThat(array).isCloseTo(doubleArrayOf(2.0, 3.0), 0.000001)

但我能做的最好的是:

fun Assert<DoubleArray>.isCloseTo(expected: DoubleArray, delta: Double) {
    hasSameSizeAs(expected)
    this.matchesPredicate { actual : DoubleArray ->
        expected.indices.forEach { index ->
            if (!actual[index].isCloseTo(expected[index], epsilon)) {
                return@matchesPredicate false
            }
        }
        true
    }
}

fun Double.isCloseTo(expected: Double, epsilon: Double): Boolean {
    abs(this - expected) <= epsilon
}

有什么方法可以将这个 matchesPredicate 减少为类似(伪代码)的东西:

    hasElementsMatchingPredicate { index, actual ->
        actual.isCloseTo(expected, epsilon)
    }

回答标题中的问题:

assert that an array's elements each match a predicate

您可以为 DoubleArray 编写一个新的 AssertK 断言:

fun Assert<DoubleArray>.allMatchPredicate(f: (Double) -> Boolean) = given { actual ->
    if (actual.all(f)) return
    val rejected = actual.filterNot(f)
    expected("all items to satisfy the predicate, but these items didn't: ${show(rejected)}")
}

然后像这样使用它:

@Test
fun test() {
    val positive = doubleArrayOf(1.0, 2.0, 3.0)
    val mixed = doubleArrayOf(-1.0, 1.0, -4.0)

    assertThat(positive).allMatchPredicate { it > 0.0 }
    assertThat(mixed).allMatchPredicate { it > 0.0 }
}

结果:

org.opentest4j.AssertionFailedError: expected all items to satisfy the predicate, but these items didn't: <[-1.0, -4.0]>

要比较两个 DoubleArrays,如问题 body 所要求的,您可以这样做:

fun Assert<DoubleArray>.allCloseTo(expected: DoubleArray, delta: Double) = given { actual ->
    if (actual.size != expected.size)
        expected("array to have size ${expected.size} but was ${actual.size}")

    val rejected = actual.asSequence()
        .withIndex().filterNot {
            // adapted from Assert<Double>.isCloseTo
            it.value >= expected[it.index].minus(delta) &&
                it.value <= expected[it.index].plus(delta)
        }
        .map { it.value }.toList()

    if (rejected.isNotEmpty())
        expected("all items to satisfy the predicate, but these items didn't: ${show(rejected)}")
}

用法:

@Test
fun test() {
    val same = doubleArrayOf(1.001, 2.001, 3.001)
    val diff = doubleArrayOf(1.1, 2.001, 3.1)

    val expected = doubleArrayOf(1.0, 2.0, 3.0)

    assertThat(same).allCloseTo(expected, 0.01)
    assertThat(diff).allCloseTo(expected, 0.01)
}

结果:

org.opentest4j.AssertionFailedError: expected all items to satisfy the predicate, but these items didn't: <[1.1, 3.1]>