编写检查定时行为的快速测试

时间:2015-10-08 06:36:48

标签: c++ googletest googlemock

我有以下课程(当然,这个例子是愚蠢的):

class file_handler_c {
public:
  err_t init(int msecs);
  err_t write(byte* buffer);
}

此类需要在任何init之前调用write,然后您可以将此类用于write到文件。但是,在msecs毫秒过后,write停止写入文件并返回错误。

我的问题是 - 如何为此行为创建快速单元测试?任何足够小的值都会创建一个非确定性测试,有时会因为机器上运行的其他进程而失败。但是,我希望测试尽可能快,不要包含任何sleep或类似的测试。我正在使用Google Test和Google Mock。

1 个答案:

答案 0 :(得分:0)

首先,我必须转移你的实际问题。请不要创建这样的接口。每当创建一个对象时,其所有函数都必须可以调用,直到它的生命周期结束。像“init()之类的非正式生命周期约束必须在write()之前调用,或者更糟糕的是”在析构函数“无法被编译器检查之前总是调用close(),因此容易出错。

因此我不认为,您应该尝试在测试中解决该问题,但在设计测试中的代码时。无论何时编写测试都很困难,确定您的界面存在缺陷。

我的建议是分开时间和写作。创建一个计时器类,例如这一个:

#include <memory>
#include <chrono>

template<typename T>
class Timer {
    private:
        std::chrono::milliseconds time_to_live;
        std::shared_ptr<T> timed_object;
    public:
        template<typename... Arg>
        Timer(std::chrono::milliseconds ttl, Arg... args):
          time_to_live{ttl},
          timed_object{std::make_shared<T>(args...)} {};

        std::weak_ptr<T> get() { return {timed_object}; };
        // ...
        // when the defined time is over, timed_object will be set to nullptr somehow.
};

像这样使用:

#include <chrono>
#include <iostream>
#include <fstream>

int main(int, char**)
{
    using namespace std::literals::chrono_literals;

    auto timed_ostream = Timer<std::ofstream>{42ms, "filename"};
    if( !timed_ostream.get().expired() ) {
       // os is a shared_ptr. That guarantees, that the ostream will not
       // be closed while you are still writing.
       auto os = timed_ostream.get().lock();
       (*os) << "whatever";
    } // now os will be destroyed. The ofstream might be destroyed
      // as well now, when the 42ms are over.
} // OK, here we destroy the timer and therefore the ofstream,
  // if it is still alive.

使用该界面,您可以轻松地使用除ostream之外的其他内容编写简单的测试用例,例如:一个int:

#include <chrono>
#include <cassert>

using namespace std::literals::chrono_literals;

void test_timed_object_valid_after_init()
{
    auto clock = std::chrono::high_resolution_clock{};
    auto start = clock.now();
    auto timed_int = Timer<int>{2000ms,42}; // valid for 2000ms
    assert(timed_int.get().expired()); // should still be here
} // The timer will be destroyed here. That destroys the shared_ptr
  // and the object as well. The long lifetime does not matter.

void test_timed_object_invalid_after_time()
{
    auto clock = std::chrono::high_resolution_clock{};
    auto start = clock.now();

    auto timed_int = Timer<int>{1ms,42}; // valid for 1ms

    // you did not want sleep(), so we do busy waiting. 
    // Prefer usleep() instead.

    // busy wait 1ms as exactly as possible.
    while( clock.now() - start < 1ms ) {}

    assert(timed_int.get().expired()); // should be gone now.
}

请注意,每个测试用例都会检查一个方案。不要在单个测试用例中尝试测试这两个要求。然后,您需要花费很长的时间让对象在初始化后安全地检查它是否存在,或者选择一个短的生命周期来检查它是否已经消失。

请注意:此帖子中的所有代码都应该编译,但当然可能仍然存在错误。这些留作学生的练习; - )