Как я могу сохранить преобразование произвольных значений пропеста вне тела тестового случая? - PullRequest
1 голос
/ 30 сентября 2019

Я хочу тщательно протестировать реализацию пересечения двух BTreeSet с. Я могу написать:

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 есть несколько вопросов, связанных с протестами.

1 Ответ

2 голосов
/ 30 сентября 2019

Существует встроенный BTreeSetStrategy в proptest, поэтому он относительно прост:

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);
    }
}

Некоторый синтаксис здесь не ванильный Rust, поэтому может потребоваться дальнейшееобъяснение:

  • Внутри макроса proptest! тесты являются обычными функциями Rust, за исключением того, что они также имеют доступ к синтаксису in Strategy для генерации входных данных.
  • Стратегии Proptestвстроены или определены пользователем. Один из способов определения стратегии заключается в макросе prop_compose!. Опять же, это обычная функция Rust, за исключением того, что она может иметь два списка аргументов. Первый список аргументов - это обычный ввод;второй может использовать синтаксис in Strategy и аргументы первого. Тип возврата указывает тип генерируемого значения. В этом случае кортеж из двух BTreeSet s.
  • Как вы уже догадались, корзина Proptest поставляется с Strategy реализациями для кортежей, поэтому кортеж типов, которые реализуют Strategy, сам по себе являетсяStrategy. Вот почему функцию touching_ranges можно использовать как единое целое.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...