TDD實例

TDD實例

github地址

項目中對于 TDD 的實戰,依賴的是 GoogleTest 框架

我負責編碼單元對中控提供

  • 設置編碼單元
  • 設置視頻源
  • 設置視頻輸出
  • 狀態檢測
  • 開啟通道
  • 關閉通道

這 6 個接口,中控通過 http 調用編碼單元接口,為了解耦和方便進行 TDD 測試,我們將這幾個方法寫成一個抽象類,編碼單元再實現具體的方法:

class IEventHandler {public:virtual bool StartChannel(int channel_id) = 0;virtual bool StopChannel(int channel_id) = 0;virtual bool ConfigChannel(int channel_id,const tucodec::SChannelSourceConfig &source_config) = 0;virtual bool ConfigChannel(int channel_id,const tucodec::SChannelDestConfig &dest_config) = 0;virtual bool ConfigChannel(int channel_id,const tucodec::SChannelEncoderConfig &encoder_config) = 0;virtual void DetectWorker(int channel_id, int duration,tucodec::SWorkerStatus &worker_status) = 0;};

這里 ConfigChannel 方法處理不同的參數結構體。

在測試用例中首先繼承 testing::Testpublic IEventHandler


class HttpControllerTest : public testing::Test, public IEventHandler {public:bool StartChannel(int channel_id) override {channel_id_ = channel_id;    return true;}bool StopChannel(int channel_id) override {channel_id_ = channel_id;  return true;}bool ConfigChannel(int channel_id,const tucodec::SChannelSourceConfig &source_config) override {channel_id_ = channel_id;// 其他參數省略source_config_.username = source_config.username;return true;}bool ConfigChannel(int channel_id,const tucodec::SChannelDestConfig &dest_config) override {channel_id_ = channel_id;// 其他參數省略dest_config_.port = dest_config.port;return true;}bool ConfigChannel(int channel_id,const tucodec::SChannelEncoderConfig &encoder_config) override {channel_id_ = channel_id;// 其他參數省略encoder_config_.output_h = encoder_config.output_h;  return true;}void DetectWorker(int channel_id, int duration,tucodec::SWorkerStatus &worker_status) override {channel_id_ = channel_id;};  protected:void SetUp() override {    tucodec::TuLog::Init("", "http_controller_test.log", 50, 2 * 1024 * 1024);tucodec::TuLog::Instance()->SetPriority(tucodec::kTulogDebug);   controller_.Init();}void TearDown() override {  tucodec::TuLog::Destory();}protected :  HttpController controller_{this};tucodec::SChannelSourceConfig source_config_;tucodec::SChannelDestConfig dest_config_;tucodec::SChannelEncoderConfig encoder_config_;int channel_id_{};
};TEST_F(HttpControllerTest, GetStateTest) {unsigned char get_json[] = "{\n""    \"cmd\":\"get_state\",\n""    \"chn_id\":1,\n""    \"duration\":2\n""}";controller_.ResolveData(get_json, sizeof(get_json));ASSERT_EQ(channel_id_, 1);
}TEST_F(HttpControllerTest, StartChannelTest) {unsigned char start_json[] = "{\n""    \"cmd\":\"start_chn\",\n""    \"chn_id\":1\n""}";controller_.ResolveData(start_json, sizeof(start_json));ASSERT_EQ(channel_id_, 1);
}TEST_F(HttpControllerTest, StopChannelTest) {unsigned char stop_json[] = "{\n""    \"cmd\":\"stop_chn\",\n""    \"chn_id\":2\n""}";controller_.ResolveData(stop_json, sizeof(stop_json));ASSERT_EQ(channel_id_, 2);
}TEST_F(HttpControllerTest, DestConfigTest) {unsigned char destination_json[] = "{\n""    \"cmd\":\"set_destination\",\n""    \"chn_id\":1,\n""    \"address\":\"192.168.1.221\",\n""    \"port\":554,\n""    \"destination_type\":\"onvif\",\n""    \"stream_id\":\"live1\"\n""}";controller_.ResolveData(destination_json, sizeof(destination_json));ASSERT_EQ(channel_id_, 1);ASSERT_EQ(dest_config_.address, "192.168.1.221");ASSERT_EQ(dest_config_.port, 554);ASSERT_EQ(dest_config_.destination_type, "onvif");ASSERT_EQ(dest_config_.stream_id, "live1");
}

實現具體接口,這里只需要對他進行賦值即可,將本身傳入 HttpController 中,這里我們先不測試網絡,直接調用接收到 http 請求后解析 json 的函數。再根據參數中命令選擇調用 設置編碼源還是具體哪一個接口。因為需要在回調函數中解析 json,這里就直接繼承 DataCallBack 類,在接收方法中直接調用 ResolveData

class HttpController : public tucodec::DataCallBack {public:explicit HttpController(IEventHandler* event_handler);virtual ~HttpController();bool Init(const std::string &server_ip = "0.0.0.0", int server_port = 8081);bool ResolveData(unsigned char* data, unsigned int len);void ReceiveDataCallBack(int local_busy_handle,int remote_handle,unsigned char* data,unsigned int data_length,void* user) override {};void ReceiveDataCallBack(int local_busy_handle,const std::string &remote_ip,int remote_port,unsigned char* data,unsigned int data_length,void* user) override {};void ReceiveDataCallBack(int local_busy_handle,const std::string& remote_ip,int remote_port,const std::string& path,unsigned char* data,unsigned int data_length,void* user) override {tucodec::TuLog::Instance()->Info("receive data: %s length: %d ", data, data_length); //NOLINTtucodec::Server* server = reinterpret_cast<tucodec::Server*>(user);if (ResolveData(data, data_length)) {unsigned char response[] = "{\"code\":0}";int len = sizeof(response);server->SendResponse2Client(0, response, len);} else {unsigned char response[] = "{\"code\":-1}";int len = sizeof(response);server->SendResponse2Client(0, response, len);}};void StatusCallBack(int local_busy_handle,int remote_handle,std::string remote_ip,int remote_port,int remote_status,void* user) override {};private:IEventHandler* event_handler_;tucodec::Server* server_;
};
const char *k_set_codec_source = "set_codec_source";
const char *k_set_destination = "set_destination";
const char *k_start_chn = "start_chn";
const char *k_stop_chn = "stop_chn";
const char *k_get_state = "get_state";
const char *k_service_state = "service_state";HttpController::HttpController(IEventHandler *event_handler) {event_handler_ = event_handler;server_ = nullptr;
}HttpController::~HttpController() {if (server_ != nullptr) {delete server_;server_ = nullptr;}
}bool HttpController::Init(const std::string &server_ip, int server_port) {return true;
}bool HttpController::ResolveData(unsigned char *data, unsigned int len) {tucodec::TuJson json;std::stringstream x_read_write;x_read_write.write((const char *) data, len);json.Load(x_read_write);std::string cmd = json.GetString("cmd");int channel_id = json.GetInt("chn_id");if (cmd == k_set_codec_source) {SourceCodecHandler source_codec_handler;return source_codec_handler.HandleJson(json, channel_id, event_handler_);}if (cmd == k_set_destination) {DestinationHandler destination_handler;return destination_handler.HandleJson(json, channel_id, event_handler_);}if (cmd == k_get_state) {GetStatusHandler get_status_handler;return get_status_handler.HandleJson(json, channel_id, event_handler_);}if (cmd == k_start_chn) {return event_handler_->StartChannel(channel_id);}if (cmd == k_stop_chn) {return event_handler_->StopChannel(channel_id);}return false;
}

這邊還添加了一個類專門用來處理不同 json,方便以后擴展

class IHttpHandler {public:virtual bool HandleJson(tucodec::TuJson &json, int channel_id, //NOLINTIEventHandler* event_handler_) = 0; //NOLINT
};class DestinationHandler : public IHttpHandler {public:bool HandleJson(tucodec::TuJson &json, int channel_id,IEventHandler* event_handler_) override;
};class SourceCodecHandler : public IHttpHandler {public:bool HandleJson(tucodec::TuJson &json, int channel_id,IEventHandler* event_handler_) override;
};class GetStatusHandler : public IHttpHandler {public:bool HandleJson(tucodec::TuJson &json, int channel_id,IEventHandler* event_handler_) override;
};

bool DestinationHandler::HandleJson(tucodec::TuJson &json,int channel_id, IEventHandler *event_handler_) { //NOLINTif (event_handler_ == nullptr) {return false;}tucodec::ChannelDestConfig channel_dest_config;channel_dest_config.port = json.GetInt("port");channel_dest_config.address = json.GetString("address");channel_dest_config.stream_id = json.GetString("stream_id");channel_dest_config.destination_type = json.GetString("destination_type");return event_handler_->ConfigChannel(channel_id, channel_dest_config);
}
bool SourceCodecHandler::HandleJson(tucodec::TuJson &json,int channel_id, IEventHandler *event_handler_) { //NOLINTif (event_handler_ == nullptr) {return false;}// codectucodec::ChannelEncoderConfig channel_encoder_config;// 省略部分參數channel_encoder_config.output_w = json.GetInt("output_w");if (!event_handler_->ConfigChannel(channel_id, channel_encoder_config)) {tucodec::TuLog::Instance()->Error("EncoderConfig Error!");return false;}// sourcetucodec::ChannelSourceConfig channel_source_config;channel_source_config.source_type = json.GetString("source_type");channel_source_config.address = json.GetString("address");channel_source_config.port = json.GetInt("port");channel_source_config.username = json.GetString("username");channel_source_config.password = json.GetString("password");return event_handler_->ConfigChannel(channel_id, channel_source_config);;
}bool GetStatusHandler::HandleJson(tucodec::TuJson &json, int channel_id, IEventHandler *event_handler_) {if (event_handler_ == nullptr) {return false;}tucodec::SWorkerStatus status;int  duration = json.GetInt("duration");event_handler_->DetectWorker(channel_id, duration, status);return status.encoder_working == 1 && status.source_working == 1;
}

解析接口測試完畢后,接下來加入網絡模塊。
HttpController 類中先添加成員變量 tucodec::Server *server_;,修改構造和析構

HttpController::HttpController(IEventHandler *event_handler) {event_handler_ = event_handler;server_ = nullptr;
}HttpController::~HttpController() {if (server_ != nullptr) {delete server_;server_ = nullptr;}
}

Init 方法中添加:

if (server_ == nullptr) {server_ = tucodec::NetWorkFactory::CreateHttpServer(server_ip,server_port);server_->SetDataCallBack(this,reinterpret_cast<void *>(server_));if (!server_->StartServer()) {tucodec::TuLog::Instance()->Error("start http server failed!");return false;}
}
tucodec::TuLog::Instance()->Info("start http server success!");

測試類中加入客戶端初始化以及阻塞函數:

void Wait(int time) {while (watcher_ == 0 && time > 0) {std::this_thread::sleep_for(std::chrono::seconds(1));--time;}
}protected:
void SetUp() override {watcher_ = 0;tucodec::TuLog::Init("", "http_controller_test.log", 50, 2 * 1024 * 1024);tucodec::TuLog::Instance()->SetPriority(tucodec::kTulogDebug);controller_.Init();client_ = tucodec::NetWorkFactory::CreateHttpClient("apt/v1/test",tucodec::kRequestPost);client_->ConnectServer("127.0.0.1", 8081);client_->SetDataCallBack(nullptr, nullptr);
}void TearDown() override {delete client_;client_ = nullptr;tucodec::TuLog::Destory();
}
protected :
tucodec::Client *client_ = nullptr;
HttpController controller_{this};
int watcher_{};

添加測試用例:


TEST_F(HttpControllerTest, SourceCodeConfigTest) {unsigned char source_codec_json[] = "{\n""    \"cmd\":\"set_codec_source\",\n""    \"chn_id\":1,\n""    \"source_type\":\"onvif\"\n""}";client_->SendRequest2Server(source_codec_json, sizeof(source_codec_json));Wait(2);// 省略參數ASSERT_EQ(channel_id_, 1);ASSERT_EQ(source_config_.source_type, "onvif");
}

盡量解耦和,讓業務依賴于接口,而非接口依賴于業務,每一個小模塊都可以單獨測試,在保證程序按預期穩定運行的基礎上(有測試用例的存在),再將我們的代碼進行重構,小步前進,出現問題也能快速定位,或者 revert


我們這里用的是 TestCase 級別的事件機制

gtest 提供了多種事件機制,非常方便我們在案例之前或之后做一些操作。總結一下 gtest 的事件一共有 3 種:

  1. 全局的,所有案例執行前后。
  2. TestSuite 級別的,在某一批案例中第一個案例前,最后一個案例執行后。
  3. TestCase 級別的,每個 TestCase 前后。

全局事件

要實現全局事件,必須寫一個類,繼承 testing::Environment 類,實現里面的 SetUpTearDown 方法。

  1. SetUp() 方法在所有案例執行前執行
  2. TearDown() 方法在所有案例執行后執行
class FooEnvironment : public testing::Environment
{
public:virtual void SetUp(){std::cout << "Foo FooEnvironment SetUP" << std::endl;}virtual void TearDown(){std::cout << "Foo FooEnvironment TearDown" << std::endl;}
};

當然,這樣還不夠,我們還需要告訴 gtest 添加這個全局事件,我們需要在 main 函數中通過 testing::AddGlobalTestEnvironment 方法將事件掛進來,也就是說,我們可以寫很多個這樣的類,然后將他們的事件都掛上去。

int main(int argc, char* argv[])
{testing::AddGlobalTestEnvironment(new FooEnvironment);testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}

TestSuite事件

我們需要寫一個類,繼承 testing::Test ,然后實現兩個靜態方法

  1. SetUpTestCase() 方法在第一個 TestCase 之前執行
  2. TearDownTestCase() 方法在最后一個 TestCase 之后執行
class FooTest : public testing::Test {protected:static void SetUpTestCase() {shared_resource_ = new ;}static void TearDownTestCase() {delete shared_resource_;shared_resource_ = NULL;}// Some expensive resource shared by all tests.static T* shared_resource_;
};

在編寫測試案例時,我們需要使用 TEST_F 這個宏,第一個參數必須是我們上面類的名字,代表一個 TestSuite

TEST_F(FooTest, Test1){// you can refer to shared_resource here
}
TEST_F(FooTest, Test2){// you can refer to shared_resource here
}

TestCase事件

TestCase 事件是掛在每個案例執行前后的,實現方式和上面的幾乎一樣,不過需要實現的是 SetUp 方法和 TearDown 方法:

  1. SetUp() 方法在每個 TestCase 之前執行
  2. TearDown() 方法在每個 TestCase 之后執行
class FooCalcTest:public testing::Test
{
protected:virtual void SetUp(){m_foo.Init();}virtual void TearDown(){m_foo.Finalize();}FooCalc m_foo;
};TEST_F(FooCalcTest, HandleNoneZeroInput)
{EXPECT_EQ(4, m_foo.Calc(12, 16));
}TEST_F(FooCalcTest, HandleNoneZeroInput_Error)
{EXPECT_EQ(5, m_foo.Calc(12, 16));
}

TEST_F 中使用的變量可以在初始化函數SetUp中初始化,在 TearDown 中銷毀,并且所有的 TEST_F 是互相獨立的,都是在初始化以后的狀態開始運行,一個 TEST_F 不會影響另一個 TEST_F 所使用的數據

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/448070.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/448070.shtml
英文地址,請注明出處:http://en.pswp.cn/news/448070.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

修改Sql server中列的屬性腳本

alter tablename alter column columnname varchar(100) not null 轉載于:https://www.cnblogs.com/pw/archive/2007/01/08/615062.html

推薦 21 個頂級的 Vue UI 庫

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1、Vuetify Star 數為 11K&#xff0c;提供了 80 多個 Vue.js 組件&#xff0c;這些組件是根據谷歌 Material Design 指南實現的。Vuet…

MSCRM日志配置

之前有很多人問我在MSCRM上日志怎么做&#xff0c;具體的如&#xff08;登錄日志&#xff0c;操作日志&#xff09;。個人認為操作日志確實比較難做&#xff08;不過我可以給一個思路可以用觸發器或者plugin來實現&#xff0c;不過比較麻煩&#xff0c;對系統壓力也比較大&…

機動車駕駛人科目三考試項目及合格標準

機動車駕駛人科目三考試項目及合格標準 &#xff08;2013年道路考試智能評判&#xff09; 科目三考試綜合評判標準 一般規定&#xff1a;道路駕駛技能滿分為100分&#xff0c;成績達到90分為合格。 道路駕駛技能通用評判 不合格情形&#xff1a;考試時出現下列情形之一的&#…

數據結構——數組

數組 github地址 數組基礎 數組最大的有點&#xff1a;快速查詢。索引快數組最好應用于 “索引有語義” 的情況但并非所有有語義的索引都適用于數組&#xff08;身份證號&#xff09;數組也可以處理 ”索引沒有語義“ 的情況 封裝數組類 數組類該具備的功能&#xff1a;增…

十分鐘入門 RocketMQ

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 本文首先引出消息中間件通常需要解決哪些問題&#xff0c;在解決這些問題當中會遇到什么困難&#xff0c;Apache RocketMQ作為阿里開源的…

高智商孩子14個獨有的特點

每一位家長都希望自己的孩子具有高智商&#xff0c;但據專家分析孩子的智商一種是與生俱來的&#xff0c;另一種是在2歲之前還可以提高的&#xff0c;一起來看看怎樣才能提高孩子的智商? 智商高的孩子都具有哪些特點? 提高孩子智商的方法 1、改變兒童的飲食習慣。 提高孩…

Onvif2.6.1命名空間前綴對照

Onvif2.6.1命名空間前綴對照 tds http://www.onvif.org/ver10/device/wsdl tev http://www.onvif.org/ver10/events/wsdl tls http://www.onvif.org/ver10/display/wsdl tmd http://www.onvif.org/ver10/deviceIO/wsdl timg http://www.onvif.org/ver20/imaging/wsdl trt…

使用delegate類型設計自定義事件

在C#編程中&#xff0c;除了Method和Property&#xff0c;任何Class都可以有自己的事件&#xff08;Event&#xff09;。定義和使用自定義事件的步驟如下&#xff1a; &#xff08;1&#xff09;在Class之外定義一個delegate類型&#xff0c;用于確定事件程序的接口 &#xff0…

各種學習資源 文檔、手冊 (Docker 、springboot 、Guava、git、logback 、Linux 、MQ、vue、Axios)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. Docker 中文手冊 &#xff1a;https://yeasy.gitbooks.io/docker_practice/advanced_network/bridge.html 2. RESTful java with JA…

C語言的“編譯時多態”

typeof 在 kernel 中的使用 —— C 語言的“編譯時多態” C 語言本身沒有多態的概念&#xff0c;函數沒有重載的概念。然而隨著 C 語言編寫的軟件逐漸龐大&#xff0c;越來越多地需要引入一些其他語言中的特性&#xff0c;來幫助更高效地進行開發&#xff0c;Linux kernel 是一…

看臉色知體內各積毒 有效清潔內臟妙方

觀察下五臟六腑是否中毒。 淤血、痰濕、寒氣這些不能及時排出體外&#xff0c;危害健康和精氣神的物質&#xff0c;中醫稱之為毒素&#xff0c;在鏡子里你也可以看出它們。識別之后&#xff0c;你更需要有效的內臟清潔妙方! 癥狀一&#xff1a;面色青兩側長痘黃褐斑愁云滿面…

UTC Time

整個地球分為二十四時區&#xff0c;每個時區都有自己的本地時間。在國際無線電通信場合&#xff0c;為了統一起見&#xff0c;使用一個統一的時間&#xff0c;稱為通用協調時(UTC, Universal Time Coordinated)。UTC與格林尼治平均時(GMT, Greenwich Mean Time)一樣&#xff0…

解決:Unknown custom element: <myData> - did you register the component correctly? For recursive compon

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 引用一個組件報錯&#xff1a; Unknown custom element: <myData> - did you register the component correctly?For recursi…

無處不在的container_of

無處不在的container_of linux 內核中定義了一個非常精煉的雙向循環鏈表及它的相關操作。如下所示&#xff1a; struct list_head {struct list_head* next, * prev; };ubuntu 12.04 中這個結構定義在 /usr/src/linux-headers-3.2.0-24-generic/include/linux/types.h 中&…

程序員學習能力提升三要素

摘要&#xff1a;IT技術的發展日新月異&#xff0c;新技術層出不窮&#xff0c;具有良好的學習能力&#xff0c;能及時獲取新知識、隨時補充和豐富自己&#xff0c;已成為程序員職業發展的核心競爭力。本文中&#xff0c;作者結合多年的學習經驗總結出了提高程序員學習能力的三…

時間,數字 ,字符串之間的轉換

package com.JUtils.base;import java.sql.Timestamp; import java.text.SimpleDateFormat;/*** 轉換工具類<br>* 若待轉換值為null或者出現異常&#xff0c;則使用默認值**/ public class ConvertUtils {/*** 字符串轉換為int*** param str * 待轉換的字符串* param …

宏定義及相關用法

宏定義及相關用法 歡迎各位補充 目錄 一些成熟軟件中常用的宏定義&#xff1a;使用一些內置宏跟蹤調試&#xff1a;宏定義防止使用時錯誤&#xff1a;宏與函數 帶副作用的宏參數 特殊符號&#xff1a;’#’、’##’ 1、一般用法2、當宏參數是另一個宏的時候 __VA_ARGS__與##…

解決:Cannot read property ‘component‘ of undefined ( 即 vue-router 0.x 轉化為 2.x)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 vue項目原本是用0.x版本的vue-router&#xff0c;但是去報出&#xff1a;Cannot read property component of undefined 這是因為版本問…

AMD Mantle再添新作,引發下代GPU架構猜想

摘要&#xff1a;今年秋天即將發布的《希德梅爾文明&#xff1a;太空》將全面支持AMD Mantle API&#xff0c;如此強大的功能背后離不開強大的CPU、GPU支持。上周AMD爆出了下一代海盜島R9 300系列&#xff0c;據網友猜測海盜島家族可能用上速度更快的HBM堆棧式內存。 小伙伴們…