c++ - std::chrono 中是否有设施来协助注入(inject) system_clock 进行单元测试

标签 c++ unit-testing c++11 c++-chrono systemtime

我依赖于可能响应也可能不响应的硬件。因此,我经常以编写带有超时的函数而告终。系统时间是脆弱单元测试的已知来源,因此注入(inject)受控且稳定的时间似乎是测试的好主意。

我想知道 std::chrono 中是否有任何工具可以帮助解决这个问题。我看到的替代方案是围绕系统时间编写一个包装器并依赖于该适配器。

这是包装器的外观的最小示例。

#pragma once
#include <memory>
#include <chrono>
#include <thread>
#include <iostream>

using std::chrono::system_clock;
using std::chrono::milliseconds;
using std::shared_ptr;
using std::make_shared;

class Wrapped_Clock
{
public:
    virtual system_clock::time_point Now() { return system_clock::now(); }
    virtual void Sleep(milliseconds ms) { std::this_thread::sleep_for(ms); }
};

class Mock_Clock : public Wrapped_Clock
{
private:
    system_clock::time_point now;
public:
    Mock_Clock() : now(system_clock::now()){}
    ~Mock_Clock() {}
    system_clock::time_point Now() { return now; }
    void Sleep(milliseconds ms) { }
};

class CanTimeOut
{
private:
    shared_ptr<Wrapped_Clock> sclock;
public:
    CanTimeOut(shared_ptr<Wrapped_Clock> sclock = make_shared<Wrapped_Clock>()) : sclock(sclock) {}
    ~CanTimeOut() {}

    milliseconds TimeoutAction(milliseconds maxtime)
    {
        using std::chrono::duration_cast;
        int x = 0;
        system_clock::time_point start = sclock->Now();
        system_clock::time_point timeout = sclock->Now() + maxtime;
        while (timeout > sclock->Now() && x != 2000)
        {
            sclock->Sleep(milliseconds(1));
            ++x;
        }
        milliseconds elapsed = duration_cast<milliseconds>(sclock->Now() - start);
        return elapsed;
    }

};

#define EXPECT_GE(left, right, test) \
{ if (!(left >= right)) { \
    std::cout << #test << " " << "!(" << left << " >= " << right << ")" << std::endl; \
} }

#define EXPECT_EQ(expected, actual, test) \
{ if (!(expected == actual)) { \
    std::cout << #test << " " << "!(" << expected << " == " << actual << ")" << std::endl; \
} }

void TestWithSystemClock()
{
    CanTimeOut cto;
    long long timeout = 1000;
    milliseconds actual = cto.TimeoutAction(milliseconds(timeout));
    EXPECT_GE(actual.count(), timeout, TestWithSystemClock);
}

void TestWithMockClock()
{
    CanTimeOut cto(make_shared<Mock_Clock>());
    milliseconds actual = cto.TimeoutAction(milliseconds(1000));
    EXPECT_EQ(0, actual.count(), TestWithMockClock);
}

int main()
{
    TestWithSystemClock();
    TestWithMockClock();
}

其中有多少可以用 std::chrone 的功能替换?

编辑 1:

  • “你到底在测试什么?” 我正在控制时间作为测试条件,以更改依赖于时间的方法调用的行为。测试说明模拟时间和控制行为作为一个概念是有效的,并显示了我对它的理解。最小示例的目的是展示我对模拟时间的理解,以便更容易地显示与 std:: 设施的差异。
  • “用大约 10 个词说明测试应该对比的内容。” 一个测试总是超时。另一个测试显示没有时间流逝。不包括控制精确和非零时间流逝的第三个测试。
  • “此外, sleep 与时钟无关。它不是计时功能。” 我需要它来确保一个测试在超时之前不会循环超过一定数量,这模拟了一些需要时间并且可能超时的 Action 。另一方面,我想建立一个快捷方式,这样第二次测试就不会浪费时间等待。也可以不模拟 sleep ,但测试需要 2 秒。我认识到 sleep 不是计时功能,因此具有误导性。

最佳答案

相反,它看起来是在模拟 std::this_thread::sleep

这有点棘手,因为它是一个只有自由函数的命名空间。很难为测试目的“注入(inject)”命名空间。因此,您确实应该用您自己的类型包装该命名空间中的函数。

我会使用静态依赖注入(inject),就像 C++:

Live On Coliru

#include <memory>
#include <chrono>
#include <thread>
#include <iostream>

using std::chrono::system_clock;
using std::chrono::milliseconds;

struct production {
    using clock = std::chrono::system_clock;

    struct this_thread {
        template<typename... A> static auto sleep_for(A&&... a) { return std::this_thread::sleep_for(std::forward<A>(a)...); }
        template<typename... A> static auto sleep_until(A&&... a) { return std::this_thread::sleep_until(std::forward<A>(a)...); }
    };
};

struct mock {
    struct clock : std::chrono::system_clock {
        using base_type = std::chrono::system_clock;
        static time_point now() { static auto onetime = base_type::now(); return onetime; }
    };

    struct this_thread {
        template<typename... A> static auto sleep_for(A&&... a) {}
        template<typename... A> static auto sleep_until(A&&... a) {}
    };
};

template <typename services = production,
         typename clock = typename services::clock,
         typename this_thread = typename services::this_thread>
class CanTimeOut
{
public:
    milliseconds TimeoutAction(milliseconds maxtime)
    {
        using std::chrono::duration_cast;

        int x = 0;
        auto start   = clock::now();
        auto timeout = clock::now() + maxtime;
        while (timeout > clock::now() && x != 2000)
        {
            this_thread::sleep_for(milliseconds(1));
            ++x;
        }
        milliseconds elapsed = duration_cast<milliseconds>(clock::now() - start);
        return elapsed;
    }

};

#define EXPECT_GE(left, right, test) \
{ if (!(left >= right)) { \
    std::cout << #test << " " << "!(" << left << " >= " << right << ")" << std::endl; \
} }

#define EXPECT_EQ(expected, actual, test) \
{ if (!(expected == actual)) { \
    std::cout << #test << " " << "!(" << expected << " == " << actual << ")" << std::endl; \
} }

void TestWithSystemClock()
{
    CanTimeOut<> cto;
    long long timeout = 1000;
    milliseconds actual = cto.TimeoutAction(milliseconds(timeout));
    EXPECT_GE(actual.count(), timeout, TestWithSystemClock);
}

void TestWithMockClock()
{
    CanTimeOut<mock> cto;
    milliseconds actual = cto.TimeoutAction(milliseconds(1000));
    EXPECT_EQ(0, actual.count(), TestWithMockClock);
}

int main()
{
    TestWithSystemClock();
    TestWithMockClock();
}

关于c++ - std::chrono 中是否有设施来协助注入(inject) system_clock 进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33606482/

相关文章:

Python 和 C++ 集成。动态库问题

c++ - 如何使用 C/C++ 有效地加入巨大的 csv 文件(100 0's of columns x 1000' 行)?

java - 用于模拟变化很大的假模型的良好单元测试实践是什么?

android - 在 Android 中测试自定义 ContentProvider

c++ - 集成多态可扩展排序顺序

c++ - 在循环中用作 "const &"函数参数的临时对象的编译器优化?

c++ - Template模板和CRTP : compiler bugs,和GCC和clang不一致

c++ - 从内存中运行进程 C/C++

c++ - Boost.Log 与 Boost.Log v2

regex - 为什么加载 Test::More 可以消除我的错误?