C/C++怎么做好單元測試

什么是單元測試

單元測試是軟件開發(fā)過程中的一種質(zhì)量保證手段。最初的來源是想模仿對硬件芯片做單元測試那樣,在軟件中也能對小的軟件單元進(jìn)行測試,從而保證軟件中某個局部設(shè)計的正確性。

傳統(tǒng)的單元測試定義

傳統(tǒng)軟件單元測試將被測單元的粒度規(guī)定為軟件中最小的功能模塊暂幼。對于C語言通常指一個函數(shù),對于Java或者C++語言通常指一個類移迫。

傳統(tǒng)做法是針對被測單元的實現(xiàn)細(xì)節(jié)進(jìn)行各種白盒測試旺嬉,即針對被測代碼的實現(xiàn)邏輯進(jìn)行各種分支測試和覆蓋測試。

傳統(tǒng)的單元測試由于缺乏自動化工具的支持厨埋,往往在測試中通過打印輸出測試結(jié)果邪媳,由人工比對每次測試是否成功。

現(xiàn)代單元測試定義

隨著技術(shù)的進(jìn)步和人們對軟件單元測試方法的發(fā)展荡陷,現(xiàn)代單元測試的定義已經(jīng)發(fā)生了很大的變化雨效。

  • 單元測試的粒度以軟件設(shè)計的松耦合邊界為粒度,不一定非要局限于函數(shù)和類這么小的粒度废赞。例如對C++的類只用對public的接口進(jìn)行測試徽龟,private接口不測試。對于C語言可以只測試每個文件對外提供服務(wù)的接口唉地,文件內(nèi)的私有輔助函數(shù)可以不測試据悔。關(guān)于測哪些不測試哪些传透,最終遵循的原則是在降低單元測試成本的情況下讓收益最大化。

  • 單元測試最好是針對被測單元的黑盒測試极颓,這樣降低由于被測代碼實現(xiàn)細(xì)節(jié)的改動朱盐,導(dǎo)致單元測試也聯(lián)動修改的頻率。

  • 借助現(xiàn)代化單元測試框架的幫助菠隆,單元測試可做到一鍵式的可反復(fù)的自動化運行兵琳。用例執(zhí)行結(jié)果的成功或失敗完全由計算機來進(jìn)行判斷,無需人工參與骇径。由于借助現(xiàn)代化單元測試框架躯肌,因此用例的編寫需要遵循測試框架的要求。

總結(jié)一下:我們認(rèn)為現(xiàn)代化的單元測試的定義應(yīng)該是:一種滿足一鍵式全自動化運行的軟件單元級別的黑盒測試破衔。

單元測試的價值

我們認(rèn)為遵循現(xiàn)代單元測試最佳實踐的單元測試過程羡榴,可以為軟件團隊帶來如下價值:

  • 單元測試可以讓軟件故障盡早地被發(fā)現(xiàn)。按照統(tǒng)計运敢,軟件故障發(fā)現(xiàn)越晚,成本呈指數(shù)趨勢上升忠售。良好的單元測試讓故障第一時間被發(fā)現(xiàn)传惠,避免故障遺留到后期由于定位修復(fù)難度帶來的更大損失。

  • 單元測試的可回歸性稻扬,為軟件提供了一層安全防護網(wǎng)卦方。這層安全防護網(wǎng)為軟件后續(xù)的重構(gòu)和修改提供了安全保障。

  • 單元測試為軟件單元如何被使用泰佳,天然提供了一份代碼樣例式的使用手冊文檔盼砍。

  • 如果能以測試驅(qū)動開發(fā)(Test Driven Development,簡稱TDD)的方式進(jìn)行單元測試逝她,那么可以把單元測試變成一種設(shè)計行為浇坐,可以驅(qū)動出更松耦合的代碼設(shè)計和實現(xiàn)。

單元測試要求

我們認(rèn)為合格的單元測試應(yīng)該滿足以下要求:

  • 測試用例要能夠一鍵式的自動化運行和自動化的結(jié)果判斷黔宛;
  • 測試用例之間不能相互依賴和干擾近刘,也就是說每個用例可以獨立地運行;
  • 測試用例是可重現(xiàn)的臀晃,也就是說在被測代碼不變的情況下觉渴,測試用例的執(zhí)行結(jié)果應(yīng)該是一致的。測試不應(yīng)該依賴不穩(wěn)定的因素:例如定時器徽惋、線程調(diào)度等等案淋;
  • 測試用例應(yīng)該是簡單易于理解的,測試用例要追求可讀性险绘,這樣才能把測試用例同時作為一份接口使用文檔踢京;

單元測試工具

隨著技術(shù)的成熟誉碴,單元測試工具現(xiàn)在已經(jīng)變得很容易獲得和使用了。自從Kent Beck(敏捷軟件開發(fā)方法泰斗漱挚,極限編程和測試驅(qū)動開發(fā)的提出者)為Java語言開發(fā)并開源了JUnit框架后翔烁,一下子將單元測試帶到了一個新的境地。隨后其它語言紛紛效仿JUnit推出了自己的開源單元測試框架旨涝。人們后來對所有編程語言的這一系列框架起了個統(tǒng)一的名稱蹬屹,叫做xUnit測試框架

評判xUnit測試框架的標(biāo)準(zhǔn)

目前對于任一編程語言白华,都能找到好幾款開源的的xUnit測試框架慨默,那么如何對比并選擇合適好用的xUnit框架呢?一般從如下幾個維度去評估弧腥。

  • 支持自動檢測注冊用例:框架能否支持簡單地構(gòu)造用例并自動注冊測試用例到測試框架中厦取;
  • 支持測試Fixture:即是否支持為一組測試用例建立統(tǒng)一的腳手架,方便測試用例的上下文構(gòu)造管搪;
  • 強大的斷言系統(tǒng):是否提供強大的斷言系統(tǒng)虾攻,供使用者在用例中描述期望;
  • 靈活的Test Suite定義:可以支持靈活的對測試用例分組更鲁;
  • 測試能力:是否支持異常測試以及參數(shù)測試霎箍;
  • 測試filter定義:可以支持靈活的命令行參數(shù),對運行用例進(jìn)行分組和過濾澡为;
  • 測試結(jié)果及報表生成:是否可以生成易于閱讀的測試結(jié)果報告以及報表文件漂坏;
  • 用例依賴管理:是否支持編輯用例的依賴關(guān)系,讓用例之間互相組合媒至,但是又不破壞每個用例的獨立性顶别;
  • 沙盒模式:是否支持測試用例的沙盒模式,降低每個測試用例上下文清理的工作拒啰;
  • 是否開源驯绎,包括公開的文檔和社區(qū)的支持是否全面;

主流C/C++ xUnit測試框架對比

根據(jù)上面提到的判斷維度图呢,我們分析對比一下當(dāng)前主流的C/C++ xUnit測試框架条篷。

測試框架特性 Boost Test CppUnit Gtest TestNgpp
是否開源
自動檢測注冊 優(yōu) 優(yōu)
斷言能力 較弱 優(yōu) 優(yōu)
支持Fixture 支持 支持 支持 支持
支持Suite分組 支持 支持 支持 支持
支持用例過濾 支持 支持 支持 支持
測試報表 不支持 支持 支持 支持
測試能力 優(yōu) 優(yōu)
用例依賴管理 不支持 不支持 不支持 支持
沙盒模式 不支持 不支持 不支持 支持
社區(qū)使用程度 一般 使用程度很高 一般

通過上面的分析可以看到,主流的C++ xUnit測試框架都是開源的蛤织。其中TestNgpp功能雖然最強大赴叹,但是用戶較少。Google推出的Gtest框架使用范圍最廣指蚜,社區(qū)支持程度也最好乞巧,從功能上來說簡單易用,作為上手框架最為合適摊鸡。其它框架由于各種缺陷不建議再選用了绽媒。

Mock框架推薦

在做單元測試的時候避免不了要為被測代碼打樁蚕冬,而mock框架主要是為了簡化打樁過程。使用mock框架可以讓打樁代碼非常容易撰寫是辕,而且不會侵入實現(xiàn)代碼囤热。比如兩個測試用例需要同一個樁函數(shù):函數(shù)聲明相同但是返回值不同。在沒有mock框架的情況下解決這類問題非常麻煩获三,而mock框架則可以輕而易舉的應(yīng)對此類問題旁蔼。

Mock框架除了提供打樁的功能外,還提供其它更加強大的功能疙教。例如何以監(jiān)聽用戶對打樁代碼的調(diào)用行為棺聊,并監(jiān)控這些行為是否符合預(yù)期。

對于Java語言來說贞谓,可用的mock框架五花八門限佩,選擇范圍非常廣。但是對于C++語言來說裸弦,只有兩款易用的mock框架:gmock和mockcpp祟同。這兩款都是開源軟件,經(jīng)過使用對比理疙,mockcpp功能強大且用戶體驗勝過gmock耐亏,所以基本沒有什么好對比和推薦的,如果需要直接上mockcpp就好了沪斟。

單元測試過程

基于前面介紹的xUnit測試框架,為代碼做單元測試的過程一般分為如下主要步驟:

  • 單元測試環(huán)境搭建暇矫;
  • 單元測試編寫主之、運行,測試通過后將代碼合入代碼管理倉庫(GIT或SVN)李根;
  • 持續(xù)集成服務(wù)器根據(jù)規(guī)則統(tǒng)一運行所有已入庫的單元測試用例槽奕;

單元測試環(huán)境搭建

這一步是在每個開發(fā)人員的機器上搭建單元測試環(huán)境。需要做的步驟如下:

  • 下載gtest和mockcpp源碼房轿,按照gtest和mockcpp的構(gòu)建安裝手冊粤攒,進(jìn)行編譯安裝;
  • 針對當(dāng)前項目的構(gòu)建工具鏈和目錄結(jié)構(gòu)囱持,為單元測試編寫一個構(gòu)建腳本夯接。該腳本要能做到把被測的代碼和gtest、mockcpp以及測試用例代碼編譯構(gòu)建成一個軟件程序纷妆。該腳本需要能夠一鍵式地進(jìn)行編譯盔几、鏈接以及執(zhí)行生成的軟件程序;
  • 環(huán)境搭建好之后掩幢,寫一些簡單的例子試運行一下逊拍,確定環(huán)境安裝OK上鞠;

測試編寫過程

當(dāng)開發(fā)人員的機器上已經(jīng)搭建好單元測試工具后。接下來就可以對代碼進(jìn)行單元測試了芯丧。

一般使用xUnit框架進(jìn)行單元測試主要有以下幾個過程:

  • 建立一個單元測試的代碼文件芍阎,如果是C++的話,那就是一個普通的cpp源碼文件缨恒;

  • 選擇需要測試的對象代碼谴咸,例如某個接口函數(shù)或者某個類。在測試文件中包含待測代碼的頭文件肿轨。

  • 在測試文件里編寫測試用例寿冕,測試用例一般包含以下幾個主要部分:

    1. 為待測代碼準(zhǔn)備上下文環(huán)境。一般就是準(zhǔn)備待測代碼可以被調(diào)用的初始條件椒袍,例如準(zhǔn)備參數(shù)驼唱、創(chuàng)建類的對象等等;
    2. 調(diào)用被測代碼的接口驹暑,傳入對應(yīng)準(zhǔn)備好的參數(shù)玫恳;
    3. 根據(jù)可觀察的返回編寫斷言,描述期望中應(yīng)當(dāng)正確發(fā)生的事情优俘。例如接口應(yīng)該的返回值是什么京办,或者某一資源應(yīng)該發(fā)生的變化結(jié)果等等。
    4. 清理上下文帆焕。一般是把為該測試準(zhǔn)備的上下文清理掉惭婿,這樣做主要是為了每個測試的獨立性和不互相干擾,避免下個測試受前一個測試的上下文影響叶雹。
  • 編寫好用例后财饥,調(diào)用測試用例的構(gòu)建腳本,編譯及執(zhí)行用例折晦,看用例是否通過钥星。

  • 如果用例失敗看是用例的問題還是被測代碼的問題,修復(fù)直到用例通過满着。

  • 將編寫好的用例以及修改的代碼提交到代碼管理倉庫谦炒。

通過持續(xù)集成進(jìn)行部署

一般一個大型的軟件團隊都是多人合作開發(fā)的模式,這時會通過公共的代碼管理倉庫進(jìn)行協(xié)調(diào)风喇。項了保證代碼每次修改的安全性宁改,需要搭建持續(xù)集成服務(wù)器。持續(xù)集成服務(wù)器就是安裝了持續(xù)集成軟件(例如開源的Jenkins軟件)的機器魂莫。該機器會實時監(jiān)控代碼管理倉庫透且,一旦發(fā)現(xiàn)有新的代碼提交,就會觸發(fā)一系列用戶定義的持續(xù)集成任務(wù)(參加下面的示意圖)。

以Jenkins舉例來說秽誊,常見的可配置的持續(xù)集成任務(wù)包括:

  • 編譯構(gòu)建鲸沮;
  • PCLint檢查;
  • 運行所有單元測試锅论;
  • 代碼測試覆蓋率報表生成讼溺;
  • 運行其它自動化的測試用例:例如組件測試或者系統(tǒng)測試;

由于持續(xù)集成服務(wù)器時刻監(jiān)控代碼管理倉庫最易,一旦有新的代碼合入就立即執(zhí)行對應(yīng)的任務(wù):例如編譯怒坯、構(gòu)建、執(zhí)行所有單元測試用例等等藻懒。持續(xù)集成工具都支持結(jié)果通知的配置剔猿,當(dāng)存在某項任務(wù)失敗則通過看板或者郵件的方式通知指定負(fù)責(zé)人,這樣一旦有人提交的代碼造成編譯構(gòu)建失敗或者單元測試失敗嬉荆,就會立即被發(fā)現(xiàn)归敬。這樣就避免了低質(zhì)量的軟件合入到代碼倉庫后,到很晚才能知道的問題鄙早。

測試覆蓋率統(tǒng)計

和單元測試相關(guān)性較大的一個是測試覆蓋率報表的生成汪茧。對于C/C++,可選的測試覆蓋率工具并不多限番,見下表舱污。

工具 平臺 是否開源
Coverage Validator windows 商用
OpenCppCoverage windows 開源 (只支持VS2013以上版本)
gcov + lcov linux 開源

測試覆蓋率工具一般安裝部署在持續(xù)集成對應(yīng)的機器上,這樣每次持續(xù)集成服務(wù)器跑完測試用例后弥虐,就會根據(jù)當(dāng)前的測試運行情況自動計算出所有代碼的測試覆蓋率結(jié)果扩灯,可以詳細(xì)看到每一行代碼的的覆蓋情況。生成的報表可以自動發(fā)布成一個網(wǎng)頁霜瘪,項目中的所有人都可以看到驴剔。

單元測試注意事項

前面我們介紹了單元測試的工具和實施過程,接下來我們看看做好單元測試要注意的一些事項粥庄。

常見誤區(qū)

在實踐的過程中,發(fā)現(xiàn)經(jīng)常有團隊雖然開發(fā)了大量的單元測試豺妓,但是單元測試有效性卻很低惜互,付出了大量成本卻并沒有得到單元測試的收益×帐茫總結(jié)之后主要有以下一些原因:

  • 異常測試覆蓋不足训堆;我們不需要對被測對象的所有可能輸入都做測試,但是需要對其做等價類劃分白嘁,對于每種等價類至少需要一條測試坑鱼。常見的錯誤做法是永遠(yuǎn)只測試正常場景,對異常場景測試的很少。

  • 測試缺少斷言未状;每個測試結(jié)束后需要用斷言來設(shè)置正確的預(yù)期結(jié)果叁熔。如果斷言沒有寫全喂链,那么必然遺漏了重要的檢查點,就相當(dāng)于給安全網(wǎng)撕了個口子彭谁。見過一些極致的場景,開發(fā)人員為了完成測試用例指標(biāo)而去湊測試用例數(shù)允扇,所有用例不加斷言缠局。這樣雖然看到執(zhí)行通過的測試用例很多,測試覆蓋率也很好考润,但是全是無效用例狭园。

  • 測試設(shè)計能力不足,測試覆蓋沒有規(guī)劃糊治。理想的情況下應(yīng)該每個測試對被測代碼的覆蓋是正交的唱矛,每個測試用例覆蓋產(chǎn)品代碼的一部分,整體上防護全部俊戳。這需要有頂層的測試設(shè)計揖赴,尤其是對于后補的單元測試,頂層的測試設(shè)計可以規(guī)劃優(yōu)先級和從重點區(qū)域開始覆蓋抑胎。常見的誤區(qū)是開發(fā)人員各自加單元測試燥滑,但是遺漏了對重要區(qū)域的覆蓋。

  • 產(chǎn)品代碼設(shè)計問題阿逃,物理或者邏輯依賴太復(fù)雜铭拧,導(dǎo)致單元測試很難寫。這時需要對原有代碼邊重構(gòu)邊補充單元測試恃锉。所以說單元測試能否搞好搀菩,不僅僅是測試的問題,即使不采用TDD的方式也得對產(chǎn)品代碼中的不合理設(shè)計做優(yōu)化破托,才能讓單元測試更有效肪跋。

如何降低單元測試成本

從長期來看,降低單元測試的成本并不在于使用了更好的單元測試工具土砂,而在于降低由于被測代碼改動導(dǎo)致單元測試隨之變動的頻度州既。軟件之所以和硬件不同就在于它的軟,它的存在價值就是為了應(yīng)對變化萝映。而軟件的變化性往往越向內(nèi)傳遞越劇烈吴叶,這導(dǎo)致了軟件單元級別的設(shè)計經(jīng)常處于變更的核心旋渦之中。所以經(jīng)常見到有些軟件團隊序臂,一旦需求變化快工期緊蚌卤,很快就把單元測試拋棄掉了。被測代碼變化導(dǎo)致單元測試跟著變化不可能不發(fā)生,但是我們要通過設(shè)計降低這種聯(lián)動變化的概率逊彭,這樣才能降低單元測試的維護成本咸灿。

要讓單元測試能夠以較低成本維護,需要注意一下事項:

  • 單元測試盡可能是單元級別的黑盒測試诫龙。白盒測試和代碼實現(xiàn)細(xì)節(jié)耦合大析显,一旦代碼修改測試就要跟著改,造成重復(fù)工作量签赃。
  • 單元測試前先要理清楚被測對象的耦合關(guān)系谷异。如果被測對象的耦合關(guān)系復(fù)雜,那么測試用例需要模擬被測對象的所有耦合關(guān)系锦聊,這樣一旦被測對象的依賴關(guān)系發(fā)生變化歹嘹,測試也要跟著一起改。這時最好是先對被測代碼做一定的解耦重構(gòu)工作孔庭。
  • 如果可能盡量學(xué)習(xí)并掌握TDD的做法尺上,對新開發(fā)的代碼嘗試采用TDD,讓單元測試驅(qū)動出更好的代碼實現(xiàn)圆到,反過來也驅(qū)動出了相對更穩(wěn)定的測試用例怎抛。
  • 開發(fā)人員的代碼設(shè)計能力決定了單元測試的根本質(zhì)量,需要持續(xù)地提高開發(fā)人員的軟件編碼能力芽淡。

由上可見马绝,做好單元測試不只是掌握單元測試工具的使用就萬事大吉了。需要對開發(fā)人員的能力進(jìn)行提升挣菲,主要包括:

  • 如何合理設(shè)計軟件富稻,對軟件單元進(jìn)行劃分,設(shè)計低耦合系統(tǒng)白胀;
  • 如何設(shè)計出靠近黑盒級別的自動化單元測試用例椭赋;
  • 如何對遺留系統(tǒng)進(jìn)行解耦重構(gòu)的能力;
  • 掌握并實施TDD的能力或杠;
  • 設(shè)計合理的持續(xù)集成策略哪怔;

將單元測試與整體測試策略結(jié)合

單元測試只是軟件測試策略中的一個環(huán)節(jié),其它的還有系統(tǒng)測試向抢,集成測試认境,組件測試等。每一級的測試都有其價值和不足笋额,所以整體測試策略需要關(guān)注如何把這些測試策略整合起來,讓整體的成本收益率最好篷扩。所以從根本上需要站在全局規(guī)劃整體的測試策略兄猩,這塊可以參考敏捷測試的測試象限和金字塔模型理論,然后根據(jù)項目的實際情況制定合理的整體測試策略。

相關(guān)推薦材料和培訓(xùn)

  1. 最全的xUnit開源測試框架列表枢冤,針對每種編程語言:https://en.wikipedia.org/wiki/List_of_unit_testing_frameworks
  2. xUnit測試最佳實踐:《xUnit Test Patterns》可是說是最權(quán)威的一本書鸠姨。
  3. 測試策略規(guī)劃:《敏捷軟件測試》,測試象限和測試金字塔理論淹真。
  4. 《TDD in Embeded C》:嵌入式環(huán)境的TDD實踐指導(dǎo)讶迁。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市核蘸,隨后出現(xiàn)的幾起案子巍糯,更是在濱河造成了極大的恐慌,老刑警劉巖客扎,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祟峦,死亡現(xiàn)場離奇詭異,居然都是意外死亡徙鱼,警方通過查閱死者的電腦和手機宅楞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來袱吆,“玉大人厌衙,你說我怎么就攤上這事〗嗜蓿” “怎么了婶希?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長处铛。 經(jīng)常有香客問我饲趋,道長,這世上最難降的妖魔是什么撤蟆? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任奕塑,我火速辦了婚禮,結(jié)果婚禮上家肯,老公的妹妹穿的比我還像新娘龄砰。我一直安慰自己,他們只是感情好讨衣,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布换棚。 她就那樣靜靜地躺著,像睡著了一般反镇。 火紅的嫁衣襯著肌膚如雪固蚤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天歹茶,我揣著相機與錄音夕玩,去河邊找鬼你弦。 笑死,一個胖子當(dāng)著我的面吹牛燎孟,可吹牛的內(nèi)容都是我干的禽作。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼揩页,長吁一口氣:“原來是場噩夢啊……” “哼旷偿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起爆侣,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤萍程,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后累提,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尘喝,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年斋陪,在試婚紗的時候發(fā)現(xiàn)自己被綠了朽褪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡无虚,死狀恐怖缔赠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情友题,我是刑警寧澤嗤堰,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站度宦,受9級特大地震影響踢匣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜戈抄,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一离唬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧划鸽,春花似錦输莺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至丈冬,卻和暖如春嘱函,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背埂蕊。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工往弓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留橄浓,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓亮航,卻偏偏與公主長得像,于是被迫代替她去往敵國和親匀们。 傳聞我的和親對象是個殘疾皇子缴淋,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348