3

我想彻底测试两个BTreeSets 的交集的实现。我可以写:

use self::proptest::prelude::*;
proptest! {
    #[test]
    fn intersect_this(s1: BTreeSet<i32>, s2: BTreeSet<i32>) {
        // ...
    }
}

但这具有较差的代码覆盖率,因为代码在某些情况下是专门的,随机集不太可能命中。一种特殊情况是元素范围几乎不相交的集合(一个集合的值 <= x,另一个集合的值 >= x)。在带有假设的 Python 中(我不是一个新手),我会写:

from hypothesis import given
from hypothesis.strategies import builds, integers, sets
from typing import Set

def touching_ranges(elements: Set[int], split: int):
    return {elt for elt in elements if elt < split}.union({split}), \
           {elt for elt in elements if elt > split}.union({split})

@given(builds(touching_ranges, sets(integers()), integers()))
def test_touching_ranges(sets):
    s1, s2 = sets
    assert len(s1.intersection(s2)) == 1

在 Rust 中,我只需要将所有内容都塞入体内:

#[test]
fn touching(mut s1: BTreeSet<i32>, split: i32) {
    let mut s2 = s1.split_off(&split);
    s1.insert(split);
    s2.insert(split);
    prop_assert_eq!(s1.intersection(&s2).count(), 1);
}

如何将任意值的转换排除在测试用例主体之外?我无法理解我发现的有关策略的任何代码示例,而且 Stack Overflow 几乎没有与 proptest 相关的问题。

4

1 回答 1

4

There is a built-in BTreeSetStrategy in proptest, so it is relatively straightforward:

use proptest::prelude::*;
use std::collections::BTreeSet;

prop_compose! {
    fn touching_ranges()
                      (split: i32,
                       mut s1: BTreeSet<i32>)
                      -> (BTreeSet<i32>, BTreeSet<i32>)
    {
        let mut s2 = s1.split_off(&split);
        s1.insert(split);
        s2.insert(split);

        (s1, s2)
    }
}

proptest! {
    #[test]
    fn touching((s1, s2) in touching_ranges()) {
        assert_eq!(s1.intersection(&s2).count(), 1);
    }
}

Some syntax here is not vanilla Rust so it may need further explanation:

  • Inside the proptest! macro, the tests are normal Rust functions except they also have access to the in Strategy syntax in order to generate inputs.
  • The Proptest strategies are either built-in or user defined. One way to define a strategy is inside a prop_compose! macro. Again, this is a normal Rust function except it can have two argument lists. The first argument list is the usual input; the second one can use the in Strategy syntax and arguments from the first. The return type indicates the type of value being generated. In this case, a tuple of two BTreeSets.
  • As you may have guessed, the Proptest crate comes with Strategy implementations for tuples, so a tuple of types that implement Strategy is itself a Strategy. That's why the function touching_ranges can be used as one.
于 2019-09-30T05:52:57.527 回答