我有一个非常基本的方法调用 wait()
在条件变量上(来自 C++ 中的 <condition_variable>
。
void WaitForEvent() {
std::unique_lock<std::mutex> lock(mutex_);
while (!event_counter_) {
cond_var_.wait(lock);
}
return;
}
现在我想对该方法的交互进行单元测试。所以我想调用WaitForEvent
, 然后调用 PostEvent
它将通知条件变量并检查等待是否已停止。
void PostEvent() {
// ...
cond_var_.notify_all();
}
我如何在单元测试中最好地做到这一点?
到目前为止我想出的唯一解决方案是
- 启动一个线程调用
WaitForEvent
- 让线程在
WaitForEvent
之后设置一个标志打电话 - 调用
PostEvent
在主线程中 - 在主线程中等待 x 秒
- 检查标志是否已经设置
但是,我不喜欢我不能加入这里的话题。如果我加入,那么如果 WaitForEvent
,我的测试将被阻止没有按预期解锁。另外,我不喜欢在单元测试中增加延迟。
我的问题有更好的解决方案吗?提前致谢:)
编辑 1: 我的第一个解决方案如下所示
SomeClass some_class{};
bool has_unblocked = false;
std::thread blocking_thread([&] {
some_class.WaitForEvent();
has_unblocked = true;
});
some_class.PostEvent();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
REQUIRE(has_unblocked == true);
blocking_thread.join();
最佳答案
我在我的一些单元测试中做了类似的事情,这些单元测试涉及线程和线程之间的混搭事件/消息。您提出的策略不错。
关于您的问题的一件事很突出,您的单元测试似乎只是在测试 std::condition_variable
的行为,而不是使用 cond_var 实现的代码的行为。我总是告诉我的团队要警惕编写仅测试平台和 C++ 运行时的测试——因为我们知道那是可行的。我怀疑您的测试除了 Wait and Post 代码之外还有更多内容,但这正是我想指出的。因为一旦您意识到这一点,您就可以考虑在单元测试中覆盖 Wait 和 Post 代码。
If I join,then my tests will block if WaitForEvent doesn't unblock as expected.
但在成功案例中,这无关紧要,因为您的代码可以正常工作并且测试可以快速完成。如果它确实永远阻塞,则您的单元测试发现了一个真正的错误。这是好事吧? (除了你的团队成员提示单元测试再次挂起。)
但是,如果您想避免 UT 代码中出现死锁的可能性,您可以使用 wait_for
而不是 wait
的等待变体。产品代码可能不依赖于超时,但 UT 确实......
class SomeClass
{
bool _running_as_unittest;
void WaitForEvent() {
std::unique_lock<std::mutex> lock(mutex_);
while (!event_counter_) {
if (_running_as_unittest) {
cond_var.wait(lock, FiveSeconds); // give the test a chance to escape
if (!event_counter) {
_errorCondition = true;
}
}
else {
cond_var.wait(lock); // wait forever
}
}
return;
}
…
}
然后是你的测试代码:
SomeClass some_class{};
bool has_unblocked = false;
some_class._running_as_unittest = true;
some_class._errorCondition = false;
std::thread blocking_thread([&] {
some_class.WaitForEvent();
has_unblocked = true;
});
some_class.PostEvent();
for (int x = 0; (x < 500) && !has_unblocked; x++) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
REQUIRE(has_unblocked == true);
REQUIRE(someclass._errorCondition == false);
// todo - if has_unblocked is false, you could consider invoking `blocking_thread.native_handle` and then invoking `pthread_kill` or `TerminateThread` as appropriate. instead of invoking `join`.
blocking_thread.join();
关于c++ - 如何在 C++ 中测试条件变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55626717/