c++ - 给定两个类,我如何概率地测试等效行为

标签 c++ testing property-based-testing

假设我有两个实现相同基本 API 的类,我想测试它们是否“随机等效”1,至少在它们方法的一个子集上。

例如,我编写了自己的“列表”类 foo:list 而不是煞费苦心地为它编写一堆单元测试,我想将它与 std::list< 进行比较 作为引用。也就是说,foo::list 上的任何操作序列都应产生与 std::list 相同序列相同的结果。

我可以列出操作的名称,但希望不要比这更多的样板。可以应用于其他“行为等效”类对的通用解决方案是理想的。


1 我所说的“随机等价”是指在许多系列操作中没有观察到差异,这显然不能完全证明等价性。

最佳答案

简而言之

构造一个 foo::list 和一个 std::list ,然后在对它们执行操作时比较它们。实际上,与普通单元测试的唯一区别是您有两个容器,而不是直接对您正在测试的类型的每个操作使用 REQUIRE(),而是在您正在测试的类型上执行操作和引用类型,然后比较它们。为此,我们假设 std::list 或任何没有错误的东西。然后,我们将其用作不失败 的引用点。换句话说,如果 std::list 操作成功,foo::list 操作成功,并且它们比较相等,则操作成功。

一个例子

您知道可以用来比较状态的操作子集是什么,而我不知道,所以这是一个模拟比较函数

template <class T, class U>
bool compare_types(const T &t, const U &u)
{
    bool equivalent = true;
    //Generic comparisons here, like iterating over the elements to compare their values.
    //Of course update equal or just return false if the comparison fails.
    //If your types are not containers, perform whatever is needed to test equivalent state.
    return equivalent;
}

正如 Jarod42 所指出的,这会变得更有趣也更通用,特别是如果下面的 Op f 是一个 lambda(通用 lambda 需要 C++14):

template <class ValueT, class RefT, class TestT, class Op>
bool compare_op_with_value(RefT &t, TestT &u, Op f, const ValueT &value)
{
    if (!compare_types(t, u))
        return false;
    f(t, value);
    f(u, value);
    return compare_types(t, u);
}

您的函数可能会返回一个值:

template <class ValueT, class RefT, class TestT, class Op>
bool compare_op_with_ret(RefT &t, TestT &u, Op f)
{
    if (!compare_types(t, u))
        return false;
    ValueT ret1 = f(t);
    ValueT ret2 = f(u);
    return ret1 == ret2 && compare_types(t, u);
}

...对于可取消引用的返回类型等等。您需要为每种测试编写一个新的比较函数,但这很简单。您需要为不同的返回类型(例如迭代器)添加另一个模板参数。

然后您需要您的测试用例(我将 std::vector 替换为 foo::list 进行说明)...

TEMPLATE_TEST_CASE("StdFooCompare", "[list]", int)
{
    using std_type = std::list<TestType>;
    using foo_type = std::vector<TestType>;

    auto initializer = {0,1,2,3,4};
    std_type list1 = initializer;
    foo_type list2 = initializer;

    //testing insertion, using auto since insert() returns iterators
    auto insert_f = [](auto list, TestType value) -> auto {
        return list.insert(list.begin(), value);
    };
    REQUIRE(compare_op_with_value(list1, list2, insert_f, -1));

    //testing front(), being explicit about the return type rather than using auto
    auto front_f = [](auto list) -> TestType & {
        return list.front();
    };
    REQUIRE(compare_op_with_ret<TestType>(list1, list2, front_f));

    //continue testing along these lines
}

我可以在这上面多花几个小时,但我希望你明白了。我在这上面花了更多时间。

注意:我实际上并没有运行这段代码,因此请将其视为所有伪代码以理解这个想法,例如我可能漏掉了一个分号或类似的东西。

关于c++ - 给定两个类,我如何概率地测试等效行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58771632/

相关文章:

具有相同名称但参数不同的 C++ 方法

c++ - 循环,if 语句,使用 inputFile 的函数仅输出 0

c++ - 多线程和模板化单例竞争条件

python - 使用假设生成具有自定义值限制的列表列表

c++ - 可变长度参数列表 - 如何理解我们检索到的最后一个参数?

angularjs - Protractor :如何从下拉列表菜单中单击菜单元素

ruby - 嵌套 RSpec 测试

testing - 在运行时跳过 RSpec 测试用例

scala - 使用 scalacheck 测试有效的状态转换

python - 声明由假设生成的两个参数之间的关系