一、异步接口测试常见方法
1.runloop阻塞调用线程等待异步执行结果。例如chromium的单测,
//单测
TEST_F(HttpNetworkTransactionTest, Incomplete100ThenEOF) {
HttpRequestInfo request;
request.method = "POST";
request.url = GURL("http://www.foo.com/");
request.traffic_annotation =
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
MockRead data_reads[] = {
MockRead(SYNCHRONOUS, "HTTP/1.0 100 Continue\r\n"),
MockRead(ASYNC, 0),
};
StaticSocketDataProvider data(data_reads, base::span<MockWrite>());
session_deps_.socket_factory->AddSocketDataProvider(&data);
TestCompletionCallback callback;
int rv = trans.Start(&request, callback.callback(), NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = callback.WaitForResult();
EXPECT_THAT(rv, IsOk());
std::string response_data;
rv = ReadTransaction(&trans, &response_data);
EXPECT_THAT(rv, IsOk());
EXPECT_EQ("", response_data);
}
//WaitForResult实现
R WaitForResult() {
TestCompletionCallbackBaseInternal::WaitForResult();
return std::move(result_);
}
void TestCompletionCallbackBaseInternal::WaitForResult() {
DCHECK(!run_loop_);
if (!have_result_) {
run_loop_ = std::make_unique<base::RunLoop>(
base::RunLoop::Type::kNestableTasksAllowed);
run_loop_->Run();
run_loop_.reset();
DCHECK(have_result_);
}
have_result_ = false; // Auto-reset for next callback.
}
void RunLoop::Run(const Location& location) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// "test" tracing category is used here because in regular scenarios RunLoop
// trace events are not useful (each process normally has one RunLoop covering
// its entire lifetime) and might be confusing (they make idle processes look
// non-idle). In tests, however, creating a RunLoop is a frequent and an
// explicit action making this trace event very useful.
TRACE_EVENT("test", "RunLoop::Run", "location", location);
if (!BeforeRun())
return;
// If there is a RunLoopTimeout active then set the timeout.
// TODO(crbug.com/905412): Use real-time for Run() timeouts so that they
// can be applied even in tests which mock TimeTicks::Now().
CancelableOnceClosure cancelable_timeout;
const RunLoopTimeout* run_timeout = GetTimeoutForCurrentThread();
if (run_timeout) {
cancelable_timeout.Reset(BindOnce(&OnRunLoopTimeout, Unretained(this),
location, run_timeout->on_timeout));
origin_task_runner_->PostDelayedTask(
FROM_HERE, cancelable_timeout.callback(), run_timeout->timeout);
}
DCHECK_EQ(this, delegate_->active_run_loops_.top());
const bool application_tasks_allowed =
delegate_->active_run_loops_.size() == 1U ||
type_ == Type::kNestableTasksAllowed;
delegate_->Run(application_tasks_allowed, TimeDelta::Max());
AfterRun();
}
/*
大概思路是,声明TestCompletionCallback,并注册callback到测试接口中,然后TestCompletionCallback调用WaitForResult阻塞线程,当事件完成后,调用callback修改标记位,结束runloop阻塞
缺点:需要给项目开发异步测试工具类,如TestCompletionCallbackBase等;同时对代码具有一定的侵入性,如start接口需要提供事件完成的回调。
关键点:runloop结束的条件,一种是设置固定等待时间,然后结束loop判断异步调用结果;一种是设置标记位,loop检查标记位,当事件完成后设置标记位,停止loop。
*/
又如,iOS开发中XCode6以前的单元测试,发起异步调用后通过循环查询标志位来检查异步调用结果。
2.通过条件变量阻塞调用线程,当异步调用执行结束后通知条件变量,检测执行结果。如我自己的实现,
class ConfigServerUnitTest : public testing::Test {
public:
static void SetUpTestCase() {}
static void TearDownTestCase() {}
virtual void SetUp(void) override {}
virtual void TearDown(void) override {}
//异步测试事件
void wait_event(std::atomic_bool& event);
void notify_event(std::atomic_bool& event);
public:
static std::mutex _s_mutex;
static std::condition_variable _s_condition_variable;
};
std::mutex ConfigServerUnitTest::_s_mutex;
std::condition_variable ConfigServerUnitTest::_s_condition_variable;
void ConfigServerUnitTest::wait_event(std::atomic_bool& event) {
std::unique_lock<std::mutex> lock(_s_mutex);
while (event == false) {
_s_condition_variable.wait(lock);
}
}
void ConfigServerUnitTest::notify_event(std::atomic_bool& event) {
std::unique_lock<std::mutex> lock(_s_mutex);
event = true;
_s_condition_variable.notify_all();
}
//单元测试
TEST_F(ConfigServerUnitTest, connect_http) {
std::atomic_bool connect_finished = false;
bool connect_result = true;
std::string valid_url = "";
auto http_transmit = std::make_shared<HttpTransmit>(valid_url, true);
http_transmit->set_callback([&](int ec) {
ec ? connect_result = false : connect_result = true;
//事件完成,通知
notify_event(connect_finished);
});
http_transmit->connect(false);
//等待异步调用完成
wait_event(connect_finished);
http_transmit->release();
EXPECT_EQ(true, connect_result);
}
3.iOS提供的XCTestExpectation,尚未仔细研究,大概也是阻塞调用线程等待异步调用结果。
参考:
chromium单测,https://source.chromium.org/chromium/chromium/src/+/master:net/http/http_network_transaction_unittest.cc
二、私有方法是否需要单测,如何单元测试?
1.宏控制,私有方法的属性public和private的属性
2.friend声明