我有以下课程(当然,这个例子是愚蠢的):
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。
答案 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.
}
请注意,每个测试用例都会检查一个方案。不要在单个测试用例中尝试测试这两个要求。然后,您需要花费很长的时间让对象在初始化后安全地检查它是否存在,或者选择一个短的生命周期来检查它是否已经消失。
请注意:此帖子中的所有代码都应该编译,但当然可能仍然存在错误。这些留作学生的练习; - )