[翻譯] 時而失敗的測試

原文鏈接:Tests that sometimes fail

撒謊者即使說真話活尊,也不會被人相信 —— 伊索

軟件項目一旦有些年頭又有很大的自動化測試集隶校,有種丑陋的模式就會出現(xiàn)。

有些本來工作的測試蛹锰,變得“有時”工作深胳。開始影響甚微,“哦铜犬,那個測試啊遍烦,有時候就是會掛掉洒忧,再點一次build就好了”。但放任不管,它會很快滾雪球式的使整個測試集癱瘓担孔。

大部分開發(fā)人員都見過這個問題眶蕉。他們把這種測試叫做“不確定測試”坊萝,“易碎的測試”隶症,“隨機的測試”,“不穩(wěn)定的測試”呐粘,“脆弱的測試”满俗,“閃爍的測試”转捕,甚至“海森堡測試” (見不確定性原理

命名是件難事,看起來這種有毒的模式并沒有唯一標準的名稱唆垃。多年來在Discourse我們用各種不同的名字稱呼它五芝,為了統(tǒng)一起見,本文用易碎測試這個最常見的說法辕万。

有很多探討易碎測試問題的文章枢步。

早在2011年,Martin Fowler寫道:

非確定性測試有兩個問題渐尿,首先它沒啥用處醉途,其次它會像病毒一樣傳染徹底毀壞整個測試集。

對此我想補充說砖茸,易碎的測試對企業(yè)是不可承受的成本隘擎。它們的維修成本極高,需要數(shù)小時甚至數(shù)天的調(diào)試凉夯,而且它們阻塞持續(xù)部署流水線货葬,讓發(fā)布功能變慢。

有一點我與Martin看法不同劲够。我發(fā)現(xiàn)有時易碎的測試也是有用的震桶,可以發(fā)現(xiàn)應(yīng)用中潛在的缺陷。有些時候本來是修正易碎測試征绎,結(jié)果修復(fù)的是應(yīng)用代碼而非測試代碼尼夺。

本文我想探討在Discourse 觀察到的模式,以及我們采用的緩解策略炒瘸。

Discourse中出現(xiàn)的模式

幾個月前我們制定了一個游戲。

在我們的Discourse開發(fā)實例上我們創(chuàng)建了一個主題寝衫。每次由于易碎測試導(dǎo)致測試集掛掉我們就會把這個主題分配給當初寫這個測試的開發(fā)人顷扩。當開發(fā)搞清楚原因修復(fù)后就會發(fā)一個跟帖。

這有助于我們了解可以采取哪方法來修復(fù)易碎測試慰毅,并且增加了易碎測試問題的可見性隘截。這是非常重要的第一步。

接下來我開始根據(jù)https://review.discourse.org/tags/heisentest的修正對易碎測試進行歸類汹胃。

最近婶芭,我們構(gòu)建了一個系統(tǒng)持續(xù)的在digital ocean上跑測試集并標記易碎測試(我們會臨時禁用它)

由此發(fā)現(xiàn)了一些有趣的導(dǎo)致易碎測試的模式,值得在這里分享着饥。

硬編碼的ID

有時在測試中為了省事我們假造犀农。

user.avatar_id = 1
user.save!

# 然后修改 avatar
user.upload_custom_avatar!

# 這里錯了,  #1號上傳從來都不存在, 
# 因而我們新創(chuàng)建的avatar 的ID 是 1. 
assert(user.avatar_id != 1)  

差不多就是這個例子的樣子。

Postgres經(jīng)常使用 sequences 來產(chǎn)生新record的id宰掉。從一開始遞增呵哨。

大部分測試框架運行測試后會回滾數(shù)據(jù)庫事務(wù)赁濒,然而回滾不會重置sequence。

ActiveRecord::.transaction do
   puts User.create!.id
   # 1
   raise ActiveRecord::Rollback
puts 

puts User.create!.id
# 2

這會造成大量的易碎測試孟害。

在理想的世界中“起始狀態(tài)”應(yīng)該是原始的并且100%可預(yù)測拒炎。然而Postgres以及許多其他數(shù)據(jù)庫的這個特性意味著我們需要考慮每次都稍有不同的起始狀態(tài)。

這就是為什么當涉及數(shù)據(jù)庫時幾乎很少看到這樣的測試:

t = Topic.create!
assert(t.id == 1)

另一個很棒的簡單例子挨务。

隨機數(shù)據(jù)

偶爾易碎測試會凸顯應(yīng)用中的缺陷击你。這里有個這種測試的例子

data = SecureRandom.hex
explode if data[0] == "0"

當然現(xiàn)實中沒人會這么寫代碼谎柄。然而丁侄,有些罕見的情況下bug可能就深藏在生產(chǎn)代碼中的奇特條件下。

如果測試集使用隨機的數(shù)據(jù)就有可能暴露這個缺陷谷誓。

對數(shù)據(jù)庫順序的糟糕假定

create table test(a int)
insert test values(1)
insert test values(2)

多年來我多次看到開發(fā)人員(也包括我)錯誤地假定如果從上面的樣例數(shù)據(jù)中select第一行绒障,一定會得到1

select a from test limit 1

上面的SQL結(jié)果可能是1也可能是2捍歪,具體取決于一系列因素户辱。如果要保證順序,需要:

select a from test order by a limit 1

這種有問題的假設(shè)可能產(chǎn)生易碎測試糙臼。有些情況下庐镐,測試本身是“好的”但是它所測試的代碼大部分時間是靠僥幸工作的。

這里有一個例子变逃,以及另外一個例子必逆。

一個很好的演示:

[8] pry(main)> User.order('id desc').find_by(name: 'sam').id
  User Load (7.6ms)  SELECT  "users".* FROM "users" WHERE "users"."name" = 'sam' ORDER BY id desc LIMIT 1
=> 25527
[9] pry(main)> User.order('id').find_by(name: 'sam').id
  User Load (1.0ms)  SELECT  "users".* FROM "users" WHERE "users"."name" = 'sam' ORDER BY id LIMIT 1
=> 2498
[10] pry(main)> User.find_by(name: 'sam').id
  User Load (0.6ms)  SELECT  "users".* FROM "users" WHERE "users"."name" = 'sam' LIMIT 1
=> 9931

即使聚合索引的主鍵是id,也不能保證結(jié)果以id排序揽乱,除非明確指定名眉。

關(guān)于時間的錯誤假設(shè)

我的測試集不脆弱,除了11AM UTC到1PM UTC這段時間凰棉。

在我們的一些特殊的測試里曾經(jīng)發(fā)生過很有趣的事损拢。

如果我在9:50 am左右提交代碼,測試集有時會失敗撒犀。問題在于悉尼時間10 am是UTC時間的12 am(取決于是否夏令時)福压。這個時間正是一些報表系統(tǒng)的切換時點來把數(shù)據(jù)歸入“今天”或“昨天”集合。

這意味著如果我們把數(shù)據(jù)插入數(shù)據(jù)庫并從報表獲取集合或舞,在一天里的一個特殊時段測試會返回錯誤的數(shù)字荆姆。這對于澳大利亞人來說真是不爽而且很不公平。

這里是一個示例 (盡管同樣的代碼在以前的迭代中一直可以工作)

一般來說我們用假造時間的方法解決這類問題映凳。測試設(shè)定時間為1 pm UTC 2018胆筒,然后做些操作,在讓時間向前一點魏宽,再做其它操作腐泻,諸如此類决乎。在Ruby中我們使用我們自己的freeze time ,JavaScript中使用Sinon.JS派桩。還有很多其它的解決方案包括timecop构诚,迷人的libfaketime 等。

我還見過由于 sleep造成的問題:

sleep 0.001
assert(elapsed < 1) 

看起來很顯然sleep 1毫秒時間流逝肯定少于1秒铆惑。但是這個顯而易見的假定有時候是不正確的范嘱。在極端高負載的情況下CPU的調(diào)度可能滯后。

另一個我們遇到過的時間相關(guān)問題是超時時間不夠员魏,曾經(jīng)困擾過我們的JS測試集丑蛤。我們有很多集成測試需要一系列的事件,點擊按鈕撕阎,然后檢查屏幕上的元素受裹。我們會設(shè)定超時作為安全措施,以防止有些bug發(fā)生后JS測試停止執(zhí)行虏束,無休止的等待某個元素被渲染棉饶。但是設(shè)定正確的超時時間非常棘手。在super taxed AWS實例上Travis CI需要設(shè)置長很多的超時镇匀。有時還會與其它問題交織在一起照藻,比如資源泄露導(dǎo)致JS測試變慢結(jié)果需要越來越久的超時。

泄露的全局狀態(tài)

測試通常需要嶄新的初始狀態(tài)以保證始行為終如一汗侵。
如果測試修改了全局變量但是沒有把它重置回原始狀態(tài)幸缕,就會造成易碎測試。
這里有個例子晰韵。

class Frog
   cattr_accessor :total_jumps
   attr_accessor :jumps

   def jump
     Frog.total_jumps = (Frog.total_jumps || 0) + 1
     self.jumps = (self.jumps || 0) + 1
   end
end

# 只有作為第一個測試執(zhí)行時正確
def test_global_tracking
   assert(Frog.total_jumps.nil?)
end

def test_jumpy
   frog = Frog.new
   frog.jump
   assert(frog.jumps == 1)
end 

先運行test_jumpy 然后運行 test_global_tracking 會失敗发乔。換個順序就不會。

由于使用了分布式緩存以及各種其它全局注冊表雪猪,我們往往會碰到這類問題列疗。這是個取舍的藝術(shù),一方面我們緩存很多狀態(tài)來加速應(yīng)用浪蹂,另一方面我們不希望有不穩(wěn)定的測試集或者無法捕獲回歸問題的測試集。

為了緩解這個問題告材,我們總是以隨機順序執(zhí)行測試(這樣更容易抓到測試中的順序依賴問題)坤次。我們還有很多清理狀態(tài)的公用代碼以避免這種程序員常常遭遇的狀況。這也是個取舍的藝術(shù)斥赋,我們的清理代碼不能做的太多以至于造成測試集大幅減速缰猴。

關(guān)于環(huán)境的糟糕假設(shè)

你的測試集里大概不會有這樣的測試。

def test_disk_space
   assert(free_space_on('/') > 1.gigabyte)  //根目錄可用空間大于1G
end

這個測試是在說疤剑,在實現(xiàn)代碼的深處藏著一些因為機器狀態(tài)略有區(qū)別的程序滑绒。

這里有個我們遇到過的例子

我們有個測試用來檢查從遠端源下載圖片的內(nèi)部實現(xiàn)闷堡。然而在我們的實現(xiàn)里有個安全措施,確保只有機器上有足夠空間時才會發(fā)生這種行為疑故。這種限制意味著如果你在一臺硬盤資源緊張的機器上運行測試集杠览,有些測試就會失敗。

在我們的實現(xiàn)代碼中有基于各種環(huán)境條件的安全措施纵势,在寫測試時我們要確保對其進行聲明踱阿。

并發(fā)

Discourse 有一些依賴于多線程的子系統(tǒng)。MessageBus 使用后臺線程監(jiān)聽Redis channel钦铁,來支持網(wǎng)站的實時更新软舌,緩存同步等。“defer” 是我們的短暫延遲隊列牛曹,用來支持非常短暫的非關(guān)鍵任務(wù)佛点,這些任務(wù)可能長時間等待IO,造成對請求處理的劫持(在我們的設(shè)置中有時請求時間達到10秒乃至100秒)黎比。 我們用background scheduler處理循環(huán)任務(wù)超营。

這里有個例子.

這類問題一般來說都非常難以debug。有些情況下焰手,我們只是在測試模式中禁用一些模塊以保證一致性糟描,比如把延遲隊列改為內(nèi)聯(lián)方式。此外我們還把多線程模塊從巨石系統(tǒng)中移出书妻。我發(fā)現(xiàn)相對于執(zhí)行緩慢的巨石系統(tǒng)中的一個子模塊船响,對于一個運行只需要5秒鐘的gem,查找并修復(fù)測試集中的并發(fā)錯誤要簡單得多躲履。

我用過的另一個技巧是模擬事件循環(huán)见间,通過驅(qū)動事件脈搏在單線程中模擬多線程。Join工作線程并且等待線程結(jié)束以及很多puts調(diào)試工猜。

資源泄漏

我們的JavaScript集成測試集差不多是最難穩(wěn)定化的測試米诉。這些測試覆蓋了很多的實現(xiàn)代碼,需要在Chrome web driver中運行篷帅。如果有幾個事件handler被忘記正確地清理史侣,幾千個測試執(zhí)行后就會導(dǎo)致資源泄漏,讓本來很快的測試逐漸變慢甚至反復(fù)無常的失敗魏身。

為了解決這些問題惊橱,我們考慮在測試執(zhí)行后進行v8內(nèi)存堆dump來監(jiān)控測試集執(zhí)行后chrome的內(nèi)存使用情況。

值得注意的是箭昵,這個問題造成了讓人迷惑的狀態(tài)税朴,測試在生產(chǎn)CI上總是成功,在Travis CI環(huán)境上卻總是失敗。因為后者的資源更為緊張正林。

緩解模式

多年來我們學(xué)到了一些可用的策略來解決這個問題泡一。這些策略有些涉及編碼,有些涉及討論觅廓”侵遥可以說最重要的第一步是承認這是個問題,并且作為一個團隊一起決定怎么面對它哪亿。

與團隊開始坦誠的討論

你應(yīng)該怎么處理易碎的測試粥烁?你可以不停的運行它們直到通過。你可以刪除他們蝇棉。你可以隔離然后修復(fù)它們讨阻。你也可以當作它沒有發(fā)生。

在Discourse我們選擇隔離并修復(fù)篡殷。不過老實說钝吮,有些情況下我們選擇忽略或者干脆刪掉。

我不確定是否有完美的解決方案板辽。

???“刪除并遺忘” 可以省錢奇瘦,代價是損失一點測試覆蓋率以及修復(fù)潛在bug的機會。如果你的測試集處于非常不穩(wěn)定的狀態(tài)劲弦,這個方案能讓你很快回到正常狀態(tài)耳标。作為開發(fā)人員我們常常會不假思索的評判”刪除并遺忘“方法糟透了。這的確是劑猛藥邑跪,有些人可能會評價這是懶惰而危險次坡。然而,如果預(yù)算非常緊張這可能是你唯一的選擇画畅。我有一個很好的論證砸琅,對于相同的代碼庫,100個測試轴踱,100%通過的測試集症脂,要好過200個測試,一半一半成功率的測試集淫僻。

??“跑到它通過” 是另一種想要魚與熊掌兼得的方案诱篷。你可以讓你的構(gòu)建保持“綠燈”同時還不用去修正易碎測試。同樣雳灵,這個方案也可以被認為是有些“懶惰”兴蒸。這種方法的缺點是有可能置有問題的實現(xiàn)代碼不顧,并且因為重試使測試集變慢细办。此外,某些情況下“跑到它通過”可能在CI上總是失敗,在本地環(huán)境總是成功笑撞。那么多少次重試才是你應(yīng)該選的岛啸?2次?10次茴肥?

???♂?“置之不理” 聽起來可能會讓很多人震驚坚踩,其實出人意料的的普遍。人很難下決心放棄精心編寫的測試瓤狐。損失厭惡的本性意味著大多數(shù)人無法接受失去一個測試的主意瞬铸。他們只是說“這次構(gòu)建不太穩(wěn)定,有時候就是會失敗”然后重新開始構(gòu)建础锐。我也這樣做過嗓节。修復(fù)易碎測試有可能非常非常困難。有些情況下涉及大量環(huán)境和很多的界面皆警,比如全應(yīng)用的集成測試拦宣。要找到罪魁禍首就如同大海撈針。

??"隔離并修復(fù)" 一般是我最喜歡的方案信姓。你”跳過“測試并讓測試集一直提醒有一個被跳過的測試鸵隧。暫時損傷一些覆蓋率直到將測試修正。

并沒有放之四海皆準的準則意推。即使在Discourse我們也是處于“置之不理”和“隔離并修復(fù)”之間的地帶豆瘫。

所以說,進行一次你們準備如何對付易碎測試的內(nèi)部討論是“至關(guān)重要”的菊值。很可能你們正在處理的方式根本不是你們希望的外驱,而是逐步演變而來的行為。
討論這個問題給了你們爭取一搏的機會俊性。

除非構(gòu)建綠燈否則不能部署

多年以來在Discourse我們已經(jīng)采用持續(xù)部署略步。這是我們的最后防線。沒有這道防線我們的測試集可能早已被污染乃至毫無用處定页。

總是以隨機順序執(zhí)行測試

在Discourse的早期我們就選擇用隨機順序執(zhí)行測試趟薄,這可以暴露由于順序依賴造成的易碎測試。通過在日志中記錄產(chǎn)生隨機順序的種子典徊,我們可以重現(xiàn)因為順序依賴造成的失敗測試杭煎。

悲哀的是 rspec bisect的作用有限

當面對易碎測試時很容易作出假設(shè)他們都是由于順序造成的。順序相關(guān)的易碎測試可以很簡單的重現(xiàn)卒落。通過二分查找可以保持順序的同時減少運行測試的數(shù)量羡铲,直到找到最小的重現(xiàn)錯誤的集合。比如隨機種子7順序中#1200號測試失敗儡毕,經(jīng)過一系列自動化黑魔法操作也切,你可以找出來#22扑媚,#100,#1200的順序造成了失敗雷恃。理論上而言這個方式非常有效疆股,但是其實有兩個陷阱需要注意。

  1. 如果二分查找過程中觸發(fā)了非順序依賴造成的失敗倒槐,那么你可能不能找到所有的易碎測試旬痹。整個流程會以非常讓人困惑的結(jié)果失敗。
  2. 根據(jù)我們對自己代碼庫的了解讨越,大多數(shù)易碎測試都不是順序依賴造成的两残。所以這套操作往往是代價高昂的亂槍打鳥。

持續(xù)地獵殺易碎測試

最近在Discourse Roman Rizzi 引入了一個新系統(tǒng)來尋找易碎測試把跨。我們在云端一遍又一遍不斷運行我們的測試集人弓。每次有測試失敗就會對它們標記,在周末我們會把易碎測試標為“跳過”等待修復(fù)节猿。

這套機制增加了測試集的穩(wěn)定性票从。有些易碎測試1000次執(zhí)行才會失敗一次。如果只是每次提交才執(zhí)行測試滨嘱,要經(jīng)過很久的時間才能找到這些少見易碎測試峰鄙。

隔離易碎測試

這是你可用的最重要的工具之一。把易碎測試標為“跳過”是非常合理的方法太雨。不過還是有一些問題需要探討:

  • 是否是環(huán)境易碎而非測試本身吟榴?也許有個內(nèi)存泄漏問題而失敗的測試僅僅是觸發(fā)了閾值?
  • 你能否自信的通過某些自動化度量確定一個測試確實是易碎的囊扳?

這有點像是“藝術(shù)”而且很大程度上取決于你和你的團隊的舒適區(qū)吩翻。我的建議是更積極的隔離。多年來我遇到的情況大都是情愿更早隔離測試以免造成反復(fù)的失敗锥咸。

不斷反復(fù)地以隨機順序運行易碎測試來排查錯誤

一般來說處理易碎測試的最大困難是很難重現(xiàn)狭瞎。為了加速反饋循環(huán)我一般會循環(huán)執(zhí)行易碎測試。

100.times do
   it "should not be a flake" do
      yet_it_is_flaky
   end
end

這個簡單的技術(shù)可以幫助找到各式各樣的易碎測試搏予。有些情況下在循環(huán)中執(zhí)行多條測試更合理熊锭,有些情況下在循環(huán)前刪除數(shù)據(jù)庫和Redis從頭開始更合理。

致力于快速測試集

多年來在Discourse我們致力于加速測試集雪侥。然而這需要權(quán)衡碗殷,一方面你手頭最好的測試是覆蓋很多代碼的集成測試。你不想犧牲測試集的質(zhì)量來增加速度速缨,而是要去除大量無意義的重復(fù)工作锌妻。

快速的測試集意味著

  • 更快的發(fā)現(xiàn)易碎測試
  • 更快的調(diào)試易碎錯誤
  • 開發(fā)人員更傾向于執(zhí)行全套的測試集并可能觸發(fā)某些易碎測試

目前Discourse有11000個Ruby測試,在我的PC上單線程執(zhí)行需要5分40秒旬牲,并行執(zhí)行需要1分15秒仿粹。

要達到這樣的速度需要定期的“速度維護”搁吓。最近我們做過的有趣的事有:

  • Daniel Waterworth 向測試集中引入了test-prof并且對把很多測試改用let_it_be我們叫它fab!因為它配置功能棒極了)來提速。預(yù)配置可以很大地提高速度來獲得預(yù)置測試數(shù)據(jù)而不必被預(yù)置腳本的局限所困吭历。
  • David Taylor引入了parallel tests gem可以并行的執(zhí)行測試集擎浴,這樣我每次跑全量測試都能省下4分鐘時間。Rail 6即將原生支持并行測試毒涧,多虧Eileen M. Uchitelle 和Rails core team的努力。

除此之外整個團隊進行了大量的改進來為測試提速贝室。這始終是我們的重點契讲。

為無法重現(xiàn)的易碎測試添加專門的診斷代碼

我用來調(diào)試易碎測試的殺手锏就是增加調(diào)試代碼。

比如這個例子.

有時候不論我怎么努力都無法在本地重現(xiàn)滑频。診斷代碼意味著下次易碎測試再被觸發(fā)時捡偏,我有機會搞清楚是什么狀態(tài)導(dǎo)致了失敗。

def test_something
   make_happy(user)
   if !user.happy
      STDERR.puts "#{user.inspect}"
   end
    assert(user.happy)
end

繼續(xù)探討

你有什么有趣的關(guān)于易碎測試的故事峡迷?你的團隊用什么方法來對付它银伟?希望能在文章評論區(qū)聽到你的見解。

延伸閱讀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末绘搞,一起剝皮案震驚了整個濱河市彤避,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌夯辖,老刑警劉巖琉预,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蒿褂,居然都是意外死亡圆米,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門啄栓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娄帖,“玉大人,你說我怎么就攤上這事昙楚〗伲” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵桂肌,是天一觀的道長数焊。 經(jīng)常有香客問我,道長崎场,這世上最難降的妖魔是什么佩耳? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮谭跨,結(jié)果婚禮上干厚,老公的妹妹穿的比我還像新娘李滴。我一直安慰自己,他們只是感情好蛮瞄,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布所坯。 她就那樣靜靜地躺著,像睡著了一般挂捅。 火紅的嫁衣襯著肌膚如雪芹助。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天闲先,我揣著相機與錄音状土,去河邊找鬼。 笑死伺糠,一個胖子當著我的面吹牛蒙谓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播训桶,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼累驮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了舵揭?” 一聲冷哼從身側(cè)響起谤专,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎琉朽,沒想到半個月后毒租,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡箱叁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年墅垮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耕漱。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡算色,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出螟够,到底是詐尸還是另有隱情灾梦,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布妓笙,位于F島的核電站若河,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏寞宫。R本人自食惡果不足惜萧福,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辈赋。 院中可真熱鬧鲫忍,春花似錦膏燕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至射亏,卻和暖如春近忙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背智润。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工银锻, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人做鹰。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像鼎姐,于是被迫代替她去往敵國和親钾麸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容