Mocha 和 Chai 對 JavaScript 代碼進(jìn)行單元測試

你是否曾經(jīng)嘗試過謝蓋代碼后,導(dǎo)致其它地方出現(xiàn)問題嗎老赤?

我相信很多人都遇到過。因?yàn)檫@是幾乎不可避免的制市,特別在龐大的代碼面前抬旺。由于代碼間可能是環(huán)環(huán)相扣的,改變一處會(huì)影響另一處祥楣。

但如果這種情況不會(huì)發(fā)生呢开财?如果有一種方法能讓你知道改變后會(huì)出現(xiàn)的結(jié)果呢?這無疑是極好的误褪。因?yàn)樾薷拇a后無需擔(dān)心會(huì)破壞什么東西责鳍,從而程序出現(xiàn) bug 的概率更低,在 debug 上話費(fèi)時(shí)間更少兽间。

這就是單元測試的魅力历葛。它能自動(dòng)檢測代碼中的任何問題。在修改代碼后進(jìn)行相應(yīng)測試嘀略,若有問題恤溶,能立刻知道問題是什么,問題在哪和正確的做法是什么帜羊。這完全可以消除任何猜測咒程!

在本文,我會(huì)讓你了解如何對 JavaScript 代碼進(jìn)行單元測試讼育。而且孵坚,在本文出現(xiàn)的案例和技術(shù)可同時(shí)應(yīng)用到基于瀏覽器的代碼和 Node.js 的代碼。

[阮一峰 測試框架 Mocha 實(shí)例教程](測試框架 Mocha 實(shí)例教程)

什么是單元測試

當(dāng)你對代碼庫進(jìn)行測試時(shí)窥淆,可先取一段代碼(通常是一個(gè)函數(shù)),然后在特定情況下巍杈,驗(yàn)證其行為是否正確忧饭。而單元測試就是這方面的一種結(jié)構(gòu)化和自動(dòng)化的方法。當(dāng)然筷畦,寫的測試越多词裤,獲得的益處也更大刺洒。這也會(huì)讓你在開發(fā)時(shí)更加自信。

單元測試的核心思想是給函數(shù)特定的輸入值蒙畴,測試其行為悄泥。也就是說怀大,以特定的參數(shù)調(diào)用函數(shù),然后檢查是否得到正確的結(jié)果因俐。

// 輸入 1 和 10...
var result = Math.max(1, 10);
 
// ...應(yīng)該輸出 10
if(result !== 10) {
  throw new Error('Failed');
}

在實(shí)際中,測試有時(shí)會(huì)更復(fù)雜周偎。例如抹剩,如果你的函數(shù)含有一個(gè) Ajax 請求,那么測試就需要設(shè)定更多的東西蓉坎。當(dāng)然澳眷,“根據(jù)特定的輸入值得到特定的輸出值”原理仍然適用。

設(shè)置工具

在本文蛉艾,我們選擇 Mocha钳踊。它入門簡單,能同時(shí)適用于基于瀏覽器的測試和 Node.js 的測試勿侯,而且與其它測試工具配合同樣運(yùn)行良好拓瞪。

安裝好 Node.js 后,在你的項(xiàng)目目錄下打開 terminal 或 command line罐监。

  • 如果你想在瀏覽器上測試代碼吴藻,執(zhí)行 npm install mocha chai --save-dev。
  • 如果你想測試 Node.js 代碼弓柱,除了執(zhí)行上面那行命令沟堡,也要執(zhí)行 npm install -g mocha。

此時(shí)已經(jīng)安裝了 mocha 和 chai 包(package)矢空。Mocha 是一個(gè)運(yùn)行測試的庫航罗,而 Chai 包含一些有用的功能,我們能利用這些功能對我們的測試結(jié)果進(jìn)行驗(yàn)證屁药。

Node.js vs Browser 測試對比

下面的案例是在瀏覽器上運(yùn)行測試的粥血。如果想為你的 Node.js 應(yīng)用進(jìn)行單元測試,要遵循以下步驟酿箭。

  • 對于 Node复亏,無需測試運(yùn)行文件(test runner file)。
  • 為了引入 Chari缭嫡,需在測試文件頂部添加語句 var chai = require('chai');缔御。
  • 用 mocha 命令執(zhí)行單元測試,而不是打開瀏覽器妇蛀。
設(shè)置目錄結(jié)構(gòu)

為了讓文件結(jié)構(gòu)更清晰耕突,應(yīng)將測試文件放在主代碼文件的一個(gè)獨(dú)立目錄下笤成。這是為了方便以后添加其它類型的測試(如集成測試(integration tests)功能測試(functional tests))。

對于 JavaScript眷茁,最流行的實(shí)踐方案是在項(xiàng)目根目錄下創(chuàng)建一個(gè) test/ 文件夾炕泳。然后,將每個(gè)測試文件放置在該文件夾下上祈,如 test/someModuleTest.js培遵。另一種方案是,在 test/ 目錄下雇逞,再創(chuàng)建文件夾荤懂。但我建議盡量保持簡單——這樣能保證在后面必要時(shí)進(jìn)行(快速)修改。

設(shè)置測試運(yùn)行器(Test Runner)

為了能在瀏覽器上進(jìn)行測試塘砸,我們需要?jiǎng)?chuàng)建一個(gè)簡單的 HTML 頁面作為測試運(yùn)行頁(test runner page)节仿。該頁面會(huì)加載 Mocha、測試庫文件和實(shí)際測試文件掉蔬。為了運(yùn)行這些測試廊宪,我們只需在瀏覽器打開運(yùn)行器(runner)。

如果你使用 Node.js女轿,你可跳過這一步箭启。Node.js 的單元測試能通過命令 mocha 運(yùn)行,前提是按照我推薦的目錄結(jié)構(gòu)蛉迹。

下面是我們用于測試運(yùn)行器(test runner)的代碼傅寡。我將其存為 testrunner.html。

<!DOCTYPE html>
<html>
  <head>
    <title>Mocha Tests</title>
    <link rel="stylesheet" href="node_modules/mocha/mocha.css">
  </head>
  <body>
    <div id="mocha"></div>
    <script src="node_modules/mocha/mocha.js"></script>
    <script src="node_modules/chai/chai.js"></script>
    <script>mocha.setup('bdd')</script>
 
    <!-- load code you want to test here -->
 
    <!-- load your test files here -->
 
    <script>
      mocha.run();
    </script>
  </body>
</html>

該測試運(yùn)行器的幾個(gè)重要點(diǎn):

  • 為了讓測試結(jié)果擁有漂亮的樣式北救,我們加載了 Mocha 的 CSS 文件荐操。
  • 創(chuàng)建了一個(gè) ID 為 mochat 的 div 標(biāo)簽。測試結(jié)果將放在該標(biāo)簽內(nèi)珍策。
  • 加載 Mocha 和 Chai 腳本文件托启。由于這兩個(gè)文件是通過 npm 安裝的,它們被放在 node_modules 目錄的子文件夾下攘宙。
  • 通過調(diào)用 mocha.setup屯耸,開啟 Mocha 的測試功能(testing helpers)。
  • 然后蹭劈,加載需要的測試項(xiàng)和相應(yīng)測試的文件疗绣。盡管我們還沒在這放置任何代碼。
  • 最后铺韧,調(diào)用了 mocha.run 執(zhí)行相應(yīng)測試持痰。當(dāng)然,要確保在資源和測試文件加載完成后再調(diào)用該函數(shù)祟蚀。
基本的測試骨架

現(xiàn)在我們可以運(yùn)行測試了工窍,下面就開始寫點(diǎn)測試相關(guān)的東西吧。

首先前酿,我們創(chuàng)建一個(gè) test/arrayTest.js, 每個(gè)文件名都有其具體含義患雏,顯然它是個(gè)測試文件,并會(huì)測試 array 的基本功能罢维。

每個(gè)測試案例文件都會(huì)遵循以下基本模式淹仑,首先,有個(gè) describe 塊:

describe('this is a Array', function(){
    // Further code dor tests goes here
})

describe 用于把單獨(dú)的測試聚合在一起肺孵。其第一個(gè)參數(shù)用于指示測試什么匀借,在本例中,由于我們打算測試 array 功能平窘,我傳入一個(gè) ‘Array’ 字符串吓肋。

然后,在 describe 內(nèi)需有 it 塊:

describe('Array', function() {
  it('should start empty', function() {
    // Test implementation goes here
  });
 
  // We can have more its here
});

it 用于創(chuàng)建實(shí)際的測試瑰艘。其第一個(gè)參數(shù)是對該測試的描述是鬼,且該描述的語言應(yīng)該是人類可讀的(而非編程語言)。如在本例中紫新,“it should empty” 能很好地描述了 array 的行為均蜜。實(shí)現(xiàn)該測試的具體代碼則寫在 it 的第二個(gè)參數(shù) function 內(nèi)。

所有 Mocha 測試都以同樣的骨架編寫芒率,而且它們遵循相同的基本模式囤耳。

  • 首先,使用 describe 表明我們測試什么偶芍,如 “描述 array 該如何運(yùn)行”充择。
  • 然后,使用多個(gè) it 函數(shù)創(chuàng)建獨(dú)立的測試腋寨,每個(gè) it 應(yīng)該描述一個(gè)特定的行為聪铺,如上述的案例 “it should start empty(array 運(yùn)行前應(yīng)為空)”
編寫測試代碼

現(xiàn)在我們已經(jīng)知道如何構(gòu)造測試案例了,下面就開始更有趣的部分--實(shí)現(xiàn)測試萄窜。

由于我們的測試是 array 初始值應(yīng)為空铃剔,即我們需要?jiǎng)?chuàng)建一個(gè)數(shù)組,并確保它為空查刻。實(shí)現(xiàn)該測試非常簡單:

var assert = chai.assert;

describe("測試 array 是怎么工作", function(){
    it("應(yīng)該是一個(gè) empty"键兜, function(){
        var arr = [];

        assert.equal(arr.length, 0);
    })
});

請注意首行代碼,我們設(shè)置了 assert 變量穗泵。這樣就不用每次都輸入 chai.assert 了普气。

在 it 函數(shù)里,我們創(chuàng)建了一個(gè)數(shù)組并檢查其長度佃延。盡管簡單现诀,但很好地展示了測試是如何工作的夷磕。

首先,你有東西需要被測試——這叫 被測系統(tǒng)(System Under Test仔沿,SUT)坐桩。若有需要,則對被測系統(tǒng)進(jìn)行相應(yīng)操作封锉。對于上述案例绵跷,由于檢查數(shù)組初始值是否為空,我們沒做任何操作成福。

測試的最后步驟應(yīng)該是驗(yàn)證——對結(jié)果進(jìn)行斷言(assertion)檢查碾局。對于上述案例,我們對此使用 assert.equal奴艾。大多數(shù)斷言函數(shù)的參數(shù)順序是一致的:首先是“實(shí)際”值净当,然后是“期待”值。

實(shí)際值是測試代碼的結(jié)果握侧,因此蚯瞧,在該案例中是 arr.length。

期待值是預(yù)想的結(jié)果品擎。由于數(shù)組的初始值應(yīng)為空埋合,因此,在該案例中的期待值是 0萄传。

雖然 Chai 提供了兩種不同的斷言(assertion)編寫方式甚颂,但現(xiàn)在為了保持簡單,我們使用了 assert秀菱。當(dāng)你能熟練編寫測試時(shí)振诬,你可能更想用 expect assertions ,因?yàn)樗峁┝烁`活的操作衍菱。

運(yùn)行測試

為了運(yùn)行該測試赶么,我們需要將其添加到先前創(chuàng)建的測試運(yùn)行器文件內(nèi)。

對于 Node.js脊串,我們可以跳過此步驟辫呻,然后使用命令 mocha 執(zhí)行測試。你會(huì)在 terminal 里看到測試結(jié)果琼锋。

向運(yùn)行器添加該測試(針對瀏覽器端):

<!-- load your test files here -->
<script src="test/arrayTest.js"></script>

你一旦添加了腳本放闺,就可以加載測試運(yùn)行器頁面了(若選擇在瀏覽器進(jìn)行測試)。

測試結(jié)果

當(dāng)你運(yùn)行這些測試缕坎,其測試結(jié)果看起來和下圖類似:

image

注意:在 describe 和 it 函數(shù)的描述語句都在頁面展示出來了——測試項(xiàng)(如:should start empty)都分組放在描述(如:Array)下怖侦。當(dāng)然,也可以對 describe 塊再嵌套,以創(chuàng)建更深的子分組匾寝。

下面看看測試失敗會(huì)顯示什么搬葬。

將測試的該行代碼進(jìn)行修改:

assert.equal(arr.length, 0);

將 0 改為 1。這無疑會(huì)導(dǎo)致測試失敗旗吁,因?yàn)閿?shù)組長度不再匹配期待值踩萎。

如果你再次運(yùn)行測試,那么在測試結(jié)果中很钓,運(yùn)行錯(cuò)誤的描述將以紅色顯示。

image

測試的一項(xiàng)好處是能幫助你更快地找到 bug董栽,盡管錯(cuò)誤信息在這并不是非常詳細(xì)码倦。但是我們可以解決這個(gè)問題。

大多數(shù)斷言函數(shù)都帶有一個(gè)可選的 message 參數(shù)锭碳。該信息參數(shù)會(huì)在斷言失敗時(shí)顯示袁稽。因此我們可以利用該參數(shù),讓錯(cuò)誤信息更容易理解擒抛。

我們能像下面那樣向斷言添加 message 參數(shù):

assert.equal(arr.length, 1, 'Array length was not 0');

如果你再次運(yùn)行測試推汽,那么自定義的信息會(huì)取代默認(rèn)的信息而顯示出來。

OK歧沪,讓我們將 1 改回 0歹撒,確保測試通過。

綜合案例

到目前為止诊胞,案例都是相當(dāng)簡單的暖夭。那么下面就讓我們將學(xué)到的知識(shí)付諸實(shí)踐,看看如何測試將一段實(shí)際當(dāng)中所用到的代碼撵孤。

下面是一個(gè)將 CSS 類名添加到元素的函數(shù)迈着。我們將該函數(shù)放進(jìn)新文件 js/className.js。

function addClass(el, newClass) {
  if(el.className.indexOf(newClass) === -1) {
    el.className += newClass;
  }
}

當(dāng)元素的 className 屬性不含有新類名時(shí)邪码,才向元素添加新類名--畢竟誰想看到 <div class="hello hello hello hello">裕菠。

在最好的情況下,我們要在編寫代碼前先為該函數(shù)編寫測試闭专。但 測試驅(qū)動(dòng)開發(fā)(test-driven development) 是一個(gè)復(fù)雜的主題奴潘,因此我們現(xiàn)在僅專注于編寫測試。

開始前喻圃,讓我們重溫單元測試的基本思想:賦予函數(shù)特定的輸入值萤彩,然后驗(yàn)證函數(shù)的行為是否符合預(yù)期。所以斧拍,該函數(shù)的輸入值和行為是什么呢雀扶?

給定一個(gè)元素和一個(gè)類名:

若元素的 className 屬性未含有該類名,則應(yīng)添加。
若元素的 className 屬性已含有該類名愚墓,則不應(yīng)添加予权。
將這兩種情況轉(zhuǎn)化為兩個(gè)測試。在 test 目錄下浪册,創(chuàng)建新文件 classNameTest.js 并添加以下內(nèi)容:

describe('addClass', function() {
  it('should add class to element');
  it('should not add a class which already exists');
});

我們也可以將措詞稍微地改成 “it should do X”扫腺,雖然可讀性更強(qiáng)一點(diǎn),但本質(zhì)上仍然與我們上述語句的可讀性一致村象。根據(jù)原來的措詞聯(lián)想到相應(yīng)的測試也不難笆环。

等等,測試函數(shù)跑去哪了厚者?當(dāng)我們省略 it 的第二個(gè)參數(shù)躁劣,Mocha 會(huì)在測試結(jié)果中標(biāo)記這些測試為待測試項(xiàng)。這讓設(shè)置多個(gè)測試變得更方便——就像一個(gè)備忘錄库菲,列著打算編寫的測試账忘。

接著實(shí)現(xiàn)第一個(gè)測試。

describe('addClass', function() {
  it('should add class to element', function() {
    var element = { className: '' };
 
    addClass(element, 'test-class');
 
    assert.equal(element.className, 'test-class');
  });
 
  it('should not add a class which already exists');
});

在該測試中熙宇,我們創(chuàng)建了 element 變量鳖擒,并將其與字符串 test-class(作為元素的新類名) 作為參數(shù)傳入 addClass 函數(shù)。然后烫止,使用斷言檢查該類名是否已包含在值(element.className)里蒋荚。

再一次,我們從初始的想法出發(fā)——給定一個(gè)元素和一個(gè)類名烈拒,將類名添加到 class 列表圆裕,然后以簡單的方式將其轉(zhuǎn)化為代碼。

盡管該函數(shù)(addClass)是針對 DOM 元素的荆几,但我們在此使用了一個(gè)簡單 JS 對象(plain JS object吓妆,根據(jù) jQuery 官方定義:含有零個(gè)或多個(gè)鍵值對的對象)。是的吨铸,有時(shí)我們可以利用 JavaScript 的動(dòng)態(tài)特性行拢,以上述方式簡化測試。如果不這樣做诞吱,我們就要?jiǎng)?chuàng)建一個(gè)實(shí)際的元素舟奠,這無疑會(huì)使測試代碼變復(fù)雜。當(dāng)然房维,這還有另一個(gè)好處沼瘫,由于沒使用 DOM,該測試也能在 Node.js 運(yùn)行咙俩。

在瀏覽器運(yùn)行測試

為了在瀏覽器運(yùn)行測試耿戚,你需要在運(yùn)行器添加 className.js 和 classNameTest.js湿故。

<!-- load code you want to test here -->
<script src="js/className.js"></script>
 
<!-- load your test files here -->
<script src="test/classNameTest.js"></script>

正如下面 CodePen 中所顯示的:一個(gè)測試通過,而另一個(gè)顯示待測試膜蛔。注意:為了讓代碼運(yùn)行在 CodePen 環(huán)境下坛猪,代碼需稍作調(diào)整。

接著皂股,實(shí)現(xiàn)第二個(gè)測試…

it('should not add a class which already exists', function() {
  var element = { className: 'exists' };
 
  addClass(element, 'exists');
 
  var numClasses = element.className.split(' ').length;
  assert.equal(numClasses, 1);
});

經(jīng)常運(yùn)行測試是一種好習(xí)慣墅茉。因此,讓我們現(xiàn)在運(yùn)行測試看看會(huì)發(fā)生什么呜呐。

不出所料就斤,兩者均通過。

下面是在 CodePen 中實(shí)現(xiàn)第二個(gè)測試的例子蘑辑。

但事情沒那么簡單战转!該函數(shù)的第三種情況我們并沒有考慮到,這也是該函數(shù)的一個(gè)非常嚴(yán)重的 Bug以躯。雖然該函數(shù)只有三行代碼,但你注意到了嗎啄踊?

下面為第三種情況編寫多一個(gè)案例忧设,讓這個(gè) Bug 暴露出來。

it('should append new class after existing one', function() {
  var element = { className: 'exists' };
 
  addClass(element, 'new-class');
 
  var classes = element.className.split(' ');
  assert.equal(classes[1], 'new-class');
});

你可在下面的 CodePen 中看到颠通,這次測試失敗了址晕。導(dǎo)致該問題的原因很簡單:元素上的 CSS 類名應(yīng)以空格隔開。然而顿锰,現(xiàn)在實(shí)現(xiàn)的 addClass 并未加空格谨垃!

修復(fù)該函數(shù),讓測試通過硼控。

function addClass(el, newClass) {
  if(el.className.indexOf(newClass) !== -1) {
    return;
  }
 
  if(el.className !== '') {
    //ensure class names are separated by a space
    newClass = ' ' + newClass;
  }
 
  el.className += newClass;
}

修復(fù)后刘陶,最終在 CodePen 測試通過。

在 Node 中運(yùn)行測試

在 Node 中牢撼,只有同一文件中的內(nèi)容是可見的匙隔。由于 className.js 和 classNameTest.js 在不同文件下,我們需要一種方式將一個(gè)文件導(dǎo)出到另一個(gè)文件內(nèi)熏版。而標(biāo)準(zhǔn)的方式是通過 module.exports纷责。如果你需要復(fù)習(xí)相關(guān)知識(shí),你可以看看 Understanding module.exports and exports in Node.js撼短。

代碼本質(zhì)不變再膳,只是結(jié)構(gòu)稍微不同:

// className.js
 
module.exports = {
  addClass: function(el, newClass) {
    if(el.className.indexOf(newClass) !== -1) {
      return;
    }
 
    if(el.className !== '') {
      //ensure class names are separated by a space
      newClass = ' ' + newClass;
    }
 
    el.className += newClass;
  }
}
 
// classNameTest.js
 
var chai = require('chai');
var assert = chai.assert;
 
var className = require('../js/className.js');
var addClass = className.addClass;
 
// 文件其它部分保持不變
describe('addClass', function() {
  ...
});

正如你所看到的,測試通過曲横。

image
下一步呢喂柒?

正如你所看到的,測試不復(fù)雜也不難。與編寫 JavaScript 應(yīng)用的其它方面一樣胳喷,有一些重復(fù)的基本模式湃番。一旦你熟悉了這些,你可以一次又一次的使用它們吭露。

但這些只是單元測試的皮毛吠撮,還有很多相關(guān)知識(shí)需要學(xué)習(xí)。

  • 測試更復(fù)雜的系統(tǒng)
  • 如何處理Ajax讲竿、數(shù)據(jù)庫和其它“外部”的東西泥兰。
  • 測試驅(qū)動(dòng)開發(fā)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市题禀,隨后出現(xiàn)的幾起案子鞋诗,更是在濱河造成了極大的恐慌,老刑警劉巖迈嘹,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件削彬,死亡現(xiàn)場離奇詭異,居然都是意外死亡秀仲,警方通過查閱死者的電腦和手機(jī)融痛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來神僵,“玉大人雁刷,你說我怎么就攤上這事”@瘢” “怎么了沛励?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長炮障。 經(jīng)常有香客問我目派,道長,這世上最難降的妖魔是什么铝阐? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任址貌,我火速辦了婚禮,結(jié)果婚禮上徘键,老公的妹妹穿的比我還像新娘练对。我一直安慰自己,他們只是感情好吹害,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布螟凭。 她就那樣靜靜地躺著,像睡著了一般它呀。 火紅的嫁衣襯著肌膚如雪螺男。 梳的紋絲不亂的頭發(fā)上棒厘,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機(jī)與錄音下隧,去河邊找鬼奢人。 笑死,一個(gè)胖子當(dāng)著我的面吹牛淆院,可吹牛的內(nèi)容都是我干的何乎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼土辩,長吁一口氣:“原來是場噩夢啊……” “哼支救!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拷淘,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤各墨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后启涯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贬堵,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年结洼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扁瓢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡补君,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出昧互,到底是詐尸還是另有隱情挽铁,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布敞掘,位于F島的核電站叽掘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏玖雁。R本人自食惡果不足惜更扁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赫冬。 院中可真熱鬧浓镜,春花似錦、人聲如沸劲厌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽补鼻。三九已至哄啄,卻和暖如春雅任,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咨跌。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工沪么, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锌半。 一個(gè)月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓禽车,卻偏偏與公主長得像,于是被迫代替她去往敵國和親拳喻。 傳聞我的和親對象是個(gè)殘疾皇子哭当,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

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