測(cè)試驅(qū)動(dòng)開發(fā)(TDD)入門

測(cè)試驅(qū)動(dòng)開發(fā)棚瘟,英文全稱 Test-Driven Development(簡(jiǎn)稱 TDD)氯葬,是由Kent Beck 先生在極限編程(XP)中倡導(dǎo)的開發(fā)方法虚茶。以其倡導(dǎo)先寫測(cè)試程序谚殊,然后編碼實(shí)現(xiàn)其功能得名丧鸯。

本文不打算扯過多的理論,而是通過一個(gè)操練的方式嫩絮,帶著大家去操練一下丛肢,讓同學(xué)們切身感受一下 TDD,究竟是怎么玩的剿干。開始之前先說一下 TDD 的基本步驟摔踱。

TDD 的步驟

TDD 步驟
  1. 寫一個(gè)失敗的測(cè)試
  2. 寫一個(gè)剛好讓測(cè)試通過的代碼
  3. 重構(gòu)上面的代碼

簡(jiǎn)單設(shè)計(jì)原則

重構(gòu)可以遵循簡(jiǎn)單設(shè)計(jì)原則:

簡(jiǎn)單設(shè)計(jì)原則

簡(jiǎn)單設(shè)計(jì)原則,優(yōu)先級(jí)從上至下降低怨愤,也就是說 「通過測(cè)試」的優(yōu)先級(jí)最高派敷,其次是代碼能夠「揭示意圖」和「沒有重復(fù)」,「最少元素」則是讓我們使用最少的代碼完成這個(gè)功能撰洗。

操練

Balanced Parentheses 是我在 cyber-dojo 上最喜歡的一道練習(xí)題之一篮愉,非常適合作為 TDD 入門練習(xí)。

image

先來看一下題目:

Write a program to determine if the the parentheses (),
the brackets [], and the braces {}, in a string are balanced.

For example:

{{)(}} is not balanced because ) comes before (

({)} is not balanced because ) is not balanced between {}
and similarly the { is not balanced between ()

[({})] is balanced

{}([]) is balanced

{()}[[{}]] is balanced

我來翻譯一下:

寫一段程序來判斷字符串中的小括號(hào) () 差导,中括號(hào) [] 和大括號(hào) {} 是否是平衡的(正確閉合)试躏。

例如:

{{)(}} 是沒有閉合的,因?yàn)?) 在 ( 之前设褐。

({)} 是沒有閉合的颠蕴,因?yàn)?) 在 {} 之間沒有正確閉合,同樣 { 在 () 中間沒有正確閉合助析。

[({})] 是平衡的犀被。

{}([]) 是平衡的。

{()}[[{}]] 是平衡的外冀。

需求清楚了寡键,按照一個(gè)普通程序員的思維需要先思考一下,把需求理解透徹而且思路要完整雪隧,在沒思路的情況下完全不能動(dòng)手西轩。

而使用 TDD 首先要將需求拆分成很小的任務(wù),每個(gè)任務(wù)足夠簡(jiǎn)單脑沿、獨(dú)立藕畔,通過完成一個(gè)個(gè)小任務(wù),最終交付一個(gè)完整的功能庄拇。

這個(gè)題目起碼有兩種技術(shù)方案注服,我們先來嘗試第一種。

先來拆分第一步:

輸入一個(gè)空字符串,期望是平衡的祠汇,所以返回 true 仍秤。

我們來先寫測(cè)試:

import assert from 'assert';

describe('Parentheses', function() {
  it('如果 輸入字符串為 "" ,當(dāng)調(diào)用 Parentheses.execute()可很,則結(jié)果返回 true', () => {
    assert.equal(Parentheses.execute(''), true);
  });
});

此時(shí)運(yùn)行測(cè)試:

  1. Parentheses
    如果 輸入字符串為 "" 诗力,當(dāng)調(diào)用 Parentheses.execute(),則結(jié)果返回 true:
    ReferenceError: Parentheses is not defined
    at Context.Parentheses (test/parentheses.spec.js:5:18)

接下來寫這個(gè) case 的實(shí)現(xiàn):


export default {
  execute(str) {
    if (str === '') {
      return true;
    }
  }
};

運(yùn)行:

Parentheses
? 如果 輸入字符串為 "" 我抠,當(dāng)調(diào)用 Parentheses.execute()苇本,則結(jié)果返回 true

1 passing (1ms)

第二步:

輸入符串為 (),期望的結(jié)果是 true 菜拓。

先寫測(cè)試:

it('如果 輸入字符串為 () 瓣窄,當(dāng)調(diào)用 Parentheses.execute(),則結(jié)果返回 true', () => {
  assert.equal(Parentheses.execute('()'), true);
});

運(yùn)行纳鼎、失敯诚Α!因?yàn)槠蜻@里就不再貼報(bào)錯(cuò)結(jié)果贱鄙。

然后繼續(xù)寫實(shí)現(xiàn):

export default {
  execute(str) {
    if (str === '') {
      return true;
    }

    if (str === '()') {
      return true;
    }

    return false;
  }
};

這個(gè)實(shí)現(xiàn)雖然有點(diǎn)傻劝贸,但的確是通過了測(cè)試,回顧一下 “簡(jiǎn)單設(shè)計(jì)原則” 逗宁,以上兩步代碼都過于簡(jiǎn)單映九,沒有值得重構(gòu)的地方。

第三步:

輸入符串為 ()()瞎颗,期望的結(jié)果是 true 件甥。

測(cè)試:

it('如果 輸入字符串為 ()() ,當(dāng)調(diào)用 Parentheses.execute()哼拔,則結(jié)果返回 true', () => {
  assert.equal(Parentheses.execute('()()'), true);
});

運(yùn)行引有、失敗管挟!

實(shí)現(xiàn):


export default {
  execute(str) {
    if (str === '') {
      return true;
    }

    if (str === '()') {
      return true;
    }

    if (str === '()()') {
      return true;
    }

    return false;
  }
};

這個(gè)實(shí)現(xiàn)更傻轿曙,傻到我都不好意思往上貼,再看看 “簡(jiǎn)單設(shè)計(jì)原則” 通過測(cè)試僻孝,然后可以重構(gòu)了。

其中 if (str === '()')if (str === '()()') 看起來有些重復(fù)守谓,來看看是否可以這樣重構(gòu)一下:


export default {
  execute(str) {
    if (str === '') {
      return true;
    }

    const replacedResult = str.replace(/\(\)/gi, '');
    if (replacedResult === '') {
      return true;
    }

    return false;
  }
};

將字符串中的 () 全部替換掉穿铆,如果替換后的字符串結(jié)果等于 '' 則是正確閉合的。

運(yùn)行斋荞,通過荞雏!

我們?cè)賮碓黾右粋€(gè)case :

it('如果 輸入字符串為 ()()( ,當(dāng)調(diào)用 Parentheses.execute(),則結(jié)果返回 false', () => {
  assert.equal(Parentheses.execute('()()('), false);
});

運(yùn)行凤优,通過悦陋!

第四步

輸入符串為 [],期望的結(jié)果是 true 筑辨。

測(cè)試:

it('如果 輸入字符串為 [] 俺驶,當(dāng)調(diào)用 Parentheses.execute(),則結(jié)果返回 true', () => {
  assert.equal(Parentheses.execute('[]'), true);
});

運(yùn)行棍辕、失斈合帧!

實(shí)現(xiàn):

export default {
  execute(str) {
    if (str === '') {
      return true;
    }

    let replacedResult = str.replace(/\(\)/gi, '');
    replacedResult = replacedResult.replace(/\[\]/gi, '');
    if (replacedResult === '') {
      return true;
    }

    return false;
  }
};

運(yùn)行楚昭,通過栖袋!

正則表達(dá)式可以將兩條語句合并成一條,但是合并成一條語句的可讀性較差抚太,所以這里寫成了兩句塘幅。

第五步:

輸入符串為 {},期望的結(jié)果是 true 尿贫。

測(cè)試:

it('如果 輸入字符串為 {} 晌块,當(dāng)調(diào)用 Parentheses.execute(),則結(jié)果返回 true', () => {
  assert.equal(Parentheses.execute('{}'), true);
});

實(shí)現(xiàn):

export default {
  execute(str) {
    if (str === '') {
      return true;
    }

    let replacedResult = str.replace(/\(\)/gi, '');
    replacedResult = replacedResult.replace(/\[\]/gi, '');
    replacedResult = replacedResult.replace(/\{\}/gi, '');
    if (replacedResult === '') {
      return true;
    }

    return false;
  }
};

運(yùn)行帅霜、通過匆背!

第六步:

輸入符串為 [({})],期望的結(jié)果是 true 身冀。

寫測(cè)試:

it('如果 輸入字符串為 [({})] 钝尸,當(dāng)調(diào)用 Parentheses.execute(),則結(jié)果返回 true', () => {
  assert.equal(Parentheses.execute('[({})]'), true);
});

運(yùn)行搂根、失斦浯佟!

原因是我們的替換邏輯是有順序的剩愧,當(dāng)替換完成的結(jié)果有值猪叙,如果等于輸入值則返回 false,如果不等于輸入值則繼續(xù)替換仁卷, 這里用到了遞歸穴翩。

來修改一下實(shí)現(xiàn)代碼:

export default {
  execute(str) {
    if (str === '') {
      return true;
    }

    let replacedResult = str.replace(/\(\)/gi, '');
    replacedResult = replacedResult.replace(/\[\]/gi, '');
    replacedResult = replacedResult.replace(/\{\}/gi, '');

    if (replacedResult === '') {
      return true;
    }

    if (replacedResult === str) {
      return false;
    }

    return this.execute(replacedResult);
  }
};

運(yùn)行、通過锦积!

再添加一些測(cè)試用例:

it('如果 輸入字符串為 {}([]) 芒帕,當(dāng)調(diào)用 Parentheses.execute(),則結(jié)果返回 true', () => {
  assert.equal(Parentheses.execute('{}([])'), true);
});

it('如果 輸入字符串為 {()}[[{}]] 丰介,當(dāng)調(diào)用 Parentheses.execute()背蟆,則結(jié)果返回 true', () => {
  assert.equal(Parentheses.execute('{()}[[{}]]'), true);
});

it('如果 輸入字符串為 {{)(}} 鉴分,當(dāng)調(diào)用 Parentheses.execute(),則結(jié)果返回 false', () => {
  assert.equal(Parentheses.execute('{{)(}}'), false);
});

it('如果 輸入字符串為 ({)} 带膀,當(dāng)調(diào)用 Parentheses.execute()志珍,則結(jié)果返回 false', () => {
  assert.equal(Parentheses.execute('({)}'), false);
});

運(yùn)行、通過垛叨!

這個(gè)功能我們就這樣簡(jiǎn)單的實(shí)現(xiàn)了伦糯,因?yàn)樾枨笕绱怂赃@個(gè)方案有些簡(jiǎn)陋,甚至我們都沒有做錯(cuò)誤處理点额。在這里我們不花太多時(shí)間進(jìn)行重構(gòu)舔株,直接進(jìn)入方案二。

方案二

我們將需求擴(kuò)展一下:

輸入字符串為:

const fn = () => {
    const arr = [1, 2, 3];
    if (arr.length) {
      alert('success!');
    }
};

判斷這個(gè)字符串的括號(hào)是否正確閉合还棱。

通過剛剛 git 提交的記錄找到第二步重新拉出一個(gè)分支:

git log
git checkout <第二步的版本號(hào)> -b plan-b

運(yùn)行载慈、通過!

測(cè)試已經(jīng)有了珍手,我們直接修改實(shí)現(xiàn):


export default {
  execute(str) {
    if (str === '') {
      return true;
    }

    const pipe = [];
    for (let char of str) {
      if (char === '(') {
        pipe.push(chart);
      }

      if (char === ')') {
        pipe.pop();
      }
    }

    if (!pipe.length) return true;
    return false;
  }
};

這個(gè)括號(hào)的閉合規(guī)則是先進(jìn)后出的办铡,使用數(shù)組就 ok。

運(yùn)行琳要、通過寡具!

第三步:

上面的實(shí)現(xiàn)滿足這個(gè)任務(wù),但是有一個(gè)明顯的漏洞稚补,當(dāng)輸入只有一個(gè) ) 時(shí)童叠,期望得到返回 false ,我們?cè)黾右粋€(gè) case:

it('如果 輸入字符串為 ) 课幕,當(dāng)調(diào)用 Parentheses.execute()厦坛,則結(jié)果返回 false', () => {
  assert.equal(Parentheses.execute(')'), false);
});

運(yùn)行、失斦Ь杜秸!

再修改實(shí)現(xiàn):


export default {
  execute(str) {
    if (str === '') {
      return true;
    }

    const pipe = [];
    for (let char of str) {
      if (char === '(') {
        pipe.push(char);
      }

      if (char === ')') {
        if (pipe.pop() !== '(')
          return false;
      }
    }

    if (!pipe.length) return true;
    return false;
  }
};

運(yùn)行、通過润绎!如果 pop() 的結(jié)果不是我們放進(jìn)去管道里的值撬碟,則認(rèn)為沒有正確閉合。

重構(gòu)一下莉撇,if 語句嵌套的沒有意義:


export default {
  execute(str) {
    if (str === '') {
      return true;
    }

    const pipe = [];
    for (let char of str) {
      if (char === '(') {
        pipe.push(char);
      }

      if (char === ')' && pipe.pop() !== '(') {
        return false;
      }
    }

    if (!pipe.length) return true;
    return false;
  }
};

( ) 在程序中應(yīng)該是一組常量呢蛤,不應(yīng)當(dāng)寫成字符串,所以繼續(xù)重構(gòu):


const PARENTHESES = {
  OPEN: '(',
  CLOSE: ')'
};

export default {
  execute(str) {
    if (str === '') {
      return true;
    }

    const pipe = [];
    for (let char of str) {
      if (char === PARENTHESES.OPEN) {
        pipe.push(char);
      }

      if (char === PARENTHESES.CLOSE
          && pipe.pop() !== PARENTHESES.OPEN) {
        return false;
      }
    }

    if (!pipe.length) return true;
    return false;
  }
};

運(yùn)行稼钩、通過顾稀!

再增加幾個(gè)case:

it('如果 輸入字符串為 ()() ,當(dāng)調(diào)用 Parentheses.execute()坝撑,則結(jié)果返回 true', () => {
  assert.equal(Parentheses.execute('()()'), true);
});

it('如果 輸入字符串為 ()()( 静秆,當(dāng)調(diào)用 Parentheses.execute(),則結(jié)果返回 false', () => {
  assert.equal(Parentheses.execute('()()('), false);
});

第四步:

如果輸入字符串為 ] 巡李,這結(jié)果返回 false

測(cè)試:

it('如果 輸入字符串為 ] 抚笔,當(dāng)調(diào)用 Parentheses.execute(),則結(jié)果返回 false', () => {
  assert.equal(Parentheses.execute(']'), false);
});

運(yùn)行侨拦、失斒獬取!

這個(gè)邏輯很簡(jiǎn)單狱从,只要復(fù)制上面的邏輯就ok膨蛮。

實(shí)現(xiàn):


const PARENTHESES = {
  OPEN: '(',
  CLOSE: ')'
};

const BRACKETS = {
  OPEN: '[',
  CLOSE: ']'
};

export default {
  execute(str) {
    if (str === '') {
      return true;
    }

    const pipe = [];
    for (let char of str) {
      if (char === PARENTHESES.OPEN) {
        pipe.push(char);
      }

      if (char === PARENTHESES.CLOSE
          && pipe.pop() !== PARENTHESES.OPEN) {
        return false;
      }

      if (char === BRACKETS.OPEN) {
        pipe.push(char);
      }

      if (char === BRACKETS.CLOSE
          && pipe.pop() !== BRACKETS.OPEN) {
        return false;
      }
    }

    if (!pipe.length) return true;
    return false;
  }
};

運(yùn)行、通過季研!

接下來我們開始重構(gòu)敞葛,這兩段代碼完全重復(fù),只是判斷條件不同与涡,如果后面增加 } 邏輯也是相同惹谐,所以這里我們將重復(fù)的代碼抽成函數(shù)。


const PARENTHESES = {
  OPEN: '(',
  CLOSE: ')'
};

const BRACKETS = {
  OPEN: '[',
  CLOSE: ']'
};

const holderMap = {
  '(': PARENTHESES,
  ')': PARENTHESES,
  '[': BRACKETS,
  ']': BRACKETS,
};

const compare = (char, pipe) => {
  const holder = holderMap[char];
  if (char === holder.OPEN) {
    pipe.push(char);
  }

  if (char === holder.CLOSE
      && pipe.pop() !== holder.OPEN) {
    return false;
  }

  return true;
};

export default {
  execute(str) {
    if (str === '') {
      return true;
    }

    const pipe = [];
    for (let char of str) {
      if (!compare(char, pipe)) {
        return false;
      }
    }

    if (!pipe.length) return true;
    return false;
  }
};

運(yùn)行驼卖、通過氨肌!

第五步

輸入符串為 },期望的結(jié)果是 false 酌畜。

測(cè)試:

it('如果 輸入字符串為 } 怎囚,當(dāng)調(diào)用 Parentheses.execute(),則結(jié)果返回 false', () => {
  assert.equal(Parentheses.execute('}'), false);
});

運(yùn)行桥胞、失斂沂亍!

  1. Parentheses
    如果 輸入字符串為 } 埠戳,當(dāng)調(diào)用 Parentheses.execute()井誉,則結(jié)果返回 false:
    TypeError: Cannot read property 'OPEN' of undefined
    at compare (src/parentheses.js:22:4)
    at Object.execute (src/parentheses.js:45:12)
    at Context.it (test/parentheses.spec.js:29:48)

報(bào)錯(cuò)信息和我們期望的不符,原來是 } 字符串沒有找到對(duì)應(yīng)的 holder 會(huì)報(bào)錯(cuò)整胃,來修復(fù)一下:


const PARENTHESES = {
  OPEN: '(',
  CLOSE: ')'
};

const BRACKETS = {
  OPEN: '[',
  CLOSE: ']'
};

const holderMap = {
  '(': PARENTHESES,
  ')': PARENTHESES,
  '[': BRACKETS,
  ']': BRACKETS,
};

const compare = (char, pipe) => {
  const holder = holderMap[char];
  if (!holder) return true;
  if (char === holder.OPEN) {
    pipe.push(char);
  }

  if (char === holder.CLOSE
      && pipe.pop() !== holder.OPEN) {
    return false;
  }

  return true;
};

export default {
  execute(str) {
    if (str === '') {
      return true;
    }

    const pipe = [];
    for (let char of str) {
      if (!compare(char, pipe)) {
        return false;
      }
    }

    if (!pipe.length) return true;
    return false;
  }
};

運(yùn)行颗圣、失敗屁使!這次失敗的結(jié)果與我們期望是相同的在岂,然后再修改邏輯。


const PARENTHESES = {
  OPEN: '(',
  CLOSE: ')'
};

const BRACKETS = {
  OPEN: '[',
  CLOSE: ']'
};

const BRACES = {
  OPEN: '{',
  CLOSE: '}'
};

const holderMap = {
  '(': PARENTHESES,
  ')': PARENTHESES,
  '[': BRACKETS,
  ']': BRACKETS,
  '{': BRACES,
  '}': BRACES
};

const compare = (char, pipe) => {
  const holder = holderMap[char];
  if (!holder) return true;
  if (char === holder.OPEN) {
    pipe.push(char);
  }

  if (char === holder.CLOSE
      && pipe.pop() !== holder.OPEN) {
    return false;
  }

  return true;
};

export default {
  execute(str) {
    if (str === '') {
      return true;
    }

    const pipe = [];
    for (let char of str) {
      if (!compare(char, pipe)) {
        return false;
      }
    }

    if (!pipe.length) return true;
    return false;
  }
};

因?yàn)榍懊娴闹貥?gòu)蛮寂,增加 {} 的支持只是增加一些常量的配置蔽午。

運(yùn)行、通過酬蹋!

再增加些 case:

it('如果 輸入字符串為 [({})] 及老,當(dāng)調(diào)用 Parentheses.execute()抽莱,則結(jié)果返回 true', () => {
  assert.equal(Parentheses.execute('[({})]'), true);
});

it('如果 輸入字符串為 {}([]) ,當(dāng)調(diào)用 Parentheses.execute()骄恶,則結(jié)果返回 true', () => {
  assert.equal(Parentheses.execute('{}([])'), true);
});

it('如果 輸入字符串為 {()}[[{}]] 食铐,當(dāng)調(diào)用 Parentheses.execute(),則結(jié)果返回 true', () => {
  assert.equal(Parentheses.execute('{()}[[{}]]'), true);
});

it('如果 輸入字符串為 {{)(}} 僧鲁,當(dāng)調(diào)用 Parentheses.execute()栅表,則結(jié)果返回 false', () => {
  assert.equal(Parentheses.execute('{{)(}}'), false);
});

it('如果 輸入字符串為 ({)} 钾麸,當(dāng)調(diào)用 Parentheses.execute()踱葛,則結(jié)果返回 false', () => {
  assert.equal(Parentheses.execute('({)}'), false);
});

運(yùn)行春寿、通過!

再加最后一個(gè) case:

const inputStr = `
    const fn = () => {
      const arr = [1, 2, 3];
      if (arr.length) {
        alert('success!');
      }
    };
  `;

it(`如果 輸入字符串為 ${inputStr} 堂淡,當(dāng)調(diào)用 Parentheses.execute(),則結(jié)果返回 false`, () => {
  assert.equal(Parentheses.execute(inputStr), true);
});

完成绢淀!

總結(jié)

通過上面的練習(xí),相信大家應(yīng)該能夠感受到 TDD 的威力皆的,有興趣的同學(xué)可以不使用 TDD 將上面的功能重新實(shí)現(xiàn)一遍,對(duì)比一下兩次實(shí)現(xiàn)的時(shí)間和質(zhì)量就知道要不要學(xué)習(xí) TDD 這項(xiàng)技能费薄。

資料

https://martinfowler.com/bliki/BeckDesignRules.html

《測(cè)試驅(qū)動(dòng)開發(fā)的藝術(shù)》

本文代碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市楞抡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌召廷,老刑警劉巖凳厢,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異竞慢,居然都是意外死亡先紫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門筹煮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遮精,“玉大人,你說我怎么就攤上這事败潦”境澹” “怎么了准脂?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)眼俊。 經(jīng)常有香客問我意狠,道長(zhǎng)粟关,這世上最難降的妖魔是什么疮胖? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮闷板,結(jié)果婚禮上澎灸,老公的妹妹穿的比我還像新娘。我一直安慰自己遮晚,他們只是感情好性昭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著县遣,像睡著了一般糜颠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上萧求,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天其兴,我揣著相機(jī)與錄音,去河邊找鬼夸政。 笑死元旬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的守问。 我是一名探鬼主播匀归,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼穆端!你這毒婦竟也來了体啰?” 一聲冷哼從身側(cè)響起探越,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤枕屉,失蹤者是張志新(化名)和其女友劉穎鲤氢,沒想到半個(gè)月后西潘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喷市,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡品姓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了斤蔓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弦牡。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驾锰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出艾猜,到底是詐尸還是另有隱情匆赃,我是刑警寧澤今缚,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布瞬项,位于F島的核電站何荚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏妥衣。R本人自食惡果不足惜税手,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望艺挪。 院中可真熱鬧兵扬,春花似錦、人聲如沸掂器。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽禁漓。三九已至,卻和暖如春伶跷,著一層夾襖步出監(jiān)牢的瞬間叭莫,已是汗流浹背烁试。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工靖诗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留支示,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓促绵,卻偏偏與公主長(zhǎng)得像叙甸,于是被迫代替她去往敵國(guó)和親位衩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

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