Django搭建個(gè)人博客:自動(dòng)化測(cè)試

測(cè)試是伴隨著開(kāi)發(fā)進(jìn)行的疆栏,開(kāi)發(fā)有多久曾掂,測(cè)試就要多久。本教程已經(jīng)進(jìn)行了30多章了壁顶,都是如何測(cè)試的珠洗?當(dāng)然是runserver啦!每當(dāng)開(kāi)發(fā)新功能后若专,都需要運(yùn)行服務(wù)器许蓖,假裝自己就是用戶(hù),測(cè)試是否運(yùn)行正常调衰。

這樣的人工測(cè)試優(yōu)點(diǎn)是非常直觀膊爪,你看到的和用戶(hù)看到的是完全相同的。但是缺點(diǎn)也很明顯:

  • 效率低嚎莉。在開(kāi)發(fā)時(shí)可能你需要反復(fù)的修改代碼米酬、測(cè)試功能,這樣重復(fù)查看幾十次甚至幾百次網(wǎng)頁(yè)時(shí)會(huì)相當(dāng)?shù)淖屓藷┰辍?/li>
  • 容易遺漏bug趋箩。隨著你的項(xiàng)目越來(lái)越復(fù)雜赃额,組件之間的交互也更加復(fù)雜。修改某一個(gè)組件可能會(huì)導(dǎo)致另一個(gè)組件出現(xiàn)意想不到的bug叫确,但是在人工測(cè)試時(shí)卻很難檢查出來(lái)爬早,總不能每寫(xiě)幾行代碼就把整個(gè)網(wǎng)站統(tǒng)統(tǒng)檢查一遍吧。過(guò)了很久之后你終于發(fā)現(xiàn)了這個(gè)bug启妹,但此時(shí)你已經(jīng)搞不清它來(lái)源于什么地方了筛严。
  • 有的測(cè)試不方便進(jìn)行。比如說(shuō)有個(gè)功能饶米,限制每個(gè)用戶(hù)每天發(fā)表評(píng)論不能超過(guò)10條桨啃,人工測(cè)試就顯得比較麻煩,特別是需要反復(fù)調(diào)試的時(shí)候檬输。

為了解決人工測(cè)試的種種問(wèn)題照瘾,Django引入了Python標(biāo)準(zhǔn)庫(kù)的單元測(cè)試模塊,也就是自動(dòng)化測(cè)試了:你可以寫(xiě)一段代碼丧慈,讓代碼幫你測(cè)試N雒(程序員是最會(huì)偷懶的職業(yè)..)代碼會(huì)忠實(shí)的完成測(cè)試任務(wù)主卫,幫助你從繁重的測(cè)試工作中解脫出來(lái)。除此之外鹃愤,自動(dòng)化測(cè)試還有以下優(yōu)點(diǎn):

  • 預(yù)防錯(cuò)誤簇搅。當(dāng)應(yīng)用過(guò)于復(fù)雜時(shí),代碼的意圖會(huì)變得非常不清晰软吐,甚至你都看不懂自己寫(xiě)的代碼瘩将,這是很常見(jiàn)的。而測(cè)試就好像是從內(nèi)部審查代碼一樣凹耙,可以幫助你發(fā)現(xiàn)微小的錯(cuò)誤姿现。
  • 有利于團(tuán)隊(duì)協(xié)作。良好的測(cè)試保證其他人不會(huì)不小心破壞了你的代碼(也保證你不會(huì)不小心弄壞別人的..)⌒けВ現(xiàn)在已經(jīng)不是單打獨(dú)斗出英雄的年代了备典,想要成為優(yōu)秀的Django程序員,你必須擅長(zhǎng)編寫(xiě)測(cè)試意述!

雖然學(xué)習(xí)自動(dòng)化測(cè)試不會(huì)讓你的博客增加一絲絲的功能熊经,但是可以讓代碼更加強(qiáng)壯,所以我覺(jué)得很有必要拿出一章來(lái)專(zhuān)門(mén)講講欲险。

Django官方文檔的第5部分講測(cè)試講得非常的好,并且有中文版本匹涮。本章節(jié)就大量借鑒了官方文檔天试,也非常非常推薦讀者去拜讀。

第一個(gè)測(cè)試

給我bug然低!

為了演示測(cè)試是如何工作的喜每,讓我們首先在文章模型中寫(xiě)個(gè)有bug的方法:

article/models.py

from django.utils import timezone

class ArticlePost(models.Model):
    ...

    def was_created_recently(self):
        # 若文章是"最近"發(fā)表的,則返回 True
        diff = timezone.now() - self.created
        if diff.days <= 0 and diff.seconds < 60:
            return True
        else:
            return False

這個(gè)方法用于檢測(cè)當(dāng)前文章是否是最近發(fā)表的雳攘。

這個(gè)方法稍微擴(kuò)展一下就會(huì)變得非常實(shí)用带兜。比如可以將博文的發(fā)表日期顯示為“剛剛”、“3分鐘前”吨灭、“5小時(shí)前”等相對(duì)時(shí)間刚照,用戶(hù)體驗(yàn)將大有提升。

仔細(xì)看看喧兄,它是沒(méi)辦法正確判斷“未來(lái)”的文章的:

>>> import datetime
>>> from django.utils import timezone
>>> from article.models import ArticlePost
>>> from django.contrib.auth.models import User

# 創(chuàng)建一篇"未來(lái)"的文章
>>> future_article = ArticlePost(author=User(username='user'), title='test',body='test', created=timezone.now() + datetime.timedelta(days=30))

# 是否是“最近”發(fā)表的无畔?
>>> future_article.was_created_recently()
True

未來(lái)發(fā)生的肯定不是最近發(fā)生的,因此代碼是錯(cuò)誤的吠冤。

寫(xiě)個(gè)測(cè)試暴露bug

接下來(lái)就要寫(xiě)測(cè)試用例浑彰,將測(cè)試轉(zhuǎn)為自動(dòng)化。

還記得最初生成文章app時(shí)候的目錄結(jié)構(gòu)嗎拯辙?

article
 │  admin.py
 │  apps.py
 │  models.py
 │  tests.py
 │  views.py
 │  __init__.py
 │
 └─migrations
       └─ __init__.py

這個(gè)tests.py就是留給你寫(xiě)測(cè)試用例的地方了:

article/tests.py

from django.test import TestCase

import datetime
from django.utils import timezone
from article.models import ArticlePost
from django.contrib.auth.models import User


class ArticlePostModelTests(TestCase):

    def test_was_created_recently_with_future_article(self):
        # 若文章創(chuàng)建時(shí)間為未來(lái)郭变,返回 False
        author = User(username='user', password='test_password')
        author.save()

        future_article = ArticlePost(
            author=author,
            title='test',
            body='test',
            created=timezone.now() + datetime.timedelta(days=30)
            )

        self.assertIs(future_article.was_created_recently(), False)

基本就是把剛才在Shell中的測(cè)試代碼抄了過(guò)來(lái)。有點(diǎn)不同的是末尾這個(gè)assertIs方法,了解“斷言”的同學(xué)會(huì)對(duì)它很熟悉:它的作用是檢測(cè)方法內(nèi)的兩個(gè)參數(shù)是否完全一致诉濒,如果不是則拋出異常周伦,提醒你這個(gè)地方是有問(wèn)題滴。

接下來(lái)運(yùn)行測(cè)試:

(env) > python manage.py test

運(yùn)行結(jié)果如下:

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F
======================================================================
FAIL: test_was_created_recently_with_future_article (article.tests.ArticlePostModelTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "E:\django_project\my_blog\article\tests.py", line 19, in test_was_created_recently_with_future_article
    self.assertIs(future_article.was_created_recently(), False)
AssertionError: True is not False

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)
Destroying test database for alias 'default'...

這里面名堂就很多了:

  • 首先測(cè)試系統(tǒng)會(huì)在所有以tests開(kāi)頭的文件中尋找測(cè)試代碼
  • 所有TestCase的子類(lèi)都被認(rèn)為是測(cè)試代碼
  • 系統(tǒng)創(chuàng)建了一個(gè)特殊的數(shù)據(jù)庫(kù)供測(cè)試使用循诉,即所有測(cè)試產(chǎn)生的數(shù)據(jù)不會(huì)對(duì)你自己的數(shù)據(jù)庫(kù)造成影響
  • 類(lèi)中所有以test開(kāi)頭的方法會(huì)被認(rèn)為是測(cè)試用例
  • 在運(yùn)行測(cè)試用例時(shí)横辆,assertIs拋出異常,因?yàn)?code>True is not False
  • 完成測(cè)試后茄猫,自動(dòng)銷(xiāo)毀測(cè)試數(shù)據(jù)庫(kù)

測(cè)試系統(tǒng)明確指明了錯(cuò)誤的數(shù)量狈蚤、位置和種類(lèi)等信息,請(qǐng)讀者細(xì)細(xì)品嘗划纽。

修正bug

既然通過(guò)測(cè)試找到了bug脆侮,那接下來(lái)就要把代碼進(jìn)行修正:

article/models.py

from django.utils import timezone

class ArticlePost(models.Model):
    ...

    def was_created_recently(self):
        diff = timezone.now() - self.created
        
        # if diff.days <= 0 and diff.seconds < 60:
        if diff.days == 0 and diff.seconds >= 0 and diff.seconds < 60:
            return True
        else:
            return False

重新運(yùn)行測(cè)試:

(env) > python manage.py test

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
Destroying test database for alias 'default'...

這次代碼順利通過(guò)了測(cè)試。

可以肯定的是勇劣,在往后的開(kāi)發(fā)中靖避,這個(gè)bug不會(huì)再出現(xiàn)了,因?yàn)槟阒恍枰\(yùn)行一遍測(cè)試比默,就會(huì)立即得到警告幻捏。可以認(rèn)為項(xiàng)目的這一小部分代碼永遠(yuǎn)是安全的

更全面的測(cè)試

既然一個(gè)測(cè)試用例就可以保證一小段代碼永遠(yuǎn)安全命咐,那我寫(xiě)一堆測(cè)試豈不是可以保證整個(gè)項(xiàng)目永遠(yuǎn)安全嗎篡九?確實(shí)如此,這個(gè)買(mǎi)賣(mài)絕對(duì)是不虧的醋奠。

因此我們繼續(xù)再增加幾個(gè)測(cè)試榛臼,全面強(qiáng)化代碼:

article/tests.py

...

from django.test import TestCase

import datetime
from django.utils import timezone
from article.models import ArticlePost
from django.contrib.auth.models import User


class ArticlePostModelTests(TestCase):

    def test_was_created_recently_with_future_article(self):
        # 若文章創(chuàng)建時(shí)間為未來(lái),返回 False
        ...

    def test_was_created_recently_with_seconds_before_article(self):
        # 若文章創(chuàng)建時(shí)間為 1 分鐘內(nèi)窜司,返回 True
        author = User(username='user1', password='test_password')
        author.save()
        seconds_before_article = ArticlePost(
            author=author,
            title='test1',
            body='test1',
            created=timezone.now() - datetime.timedelta(seconds=45)
            )
        self.assertIs(seconds_before_article.was_created_recently(), True)

    def test_was_created_recently_with_hours_before_article(self):
        # 若文章創(chuàng)建時(shí)間為幾小時(shí)前沛善,返回 False
        author = User(username='user2', password='test_password')
        author.save()
        hours_before_article = ArticlePost(
            author=author,
            title='test2',
            body='test2',
            created=timezone.now() - datetime.timedelta(hours=3)
            )
        self.assertIs(hours_before_article.was_created_recently(), False)

    def test_was_created_recently_with_days_before_article(self):
        # 若文章創(chuàng)建時(shí)間為幾天前,返回 False
        author = User(username='user3', password='test_password')
        author.save()
        months_before_article = ArticlePost(
            author=author,
            title='test3',
            body='test3',
            created=timezone.now() - datetime.timedelta(days=5)
            )
        self.assertIs(months_before_article.was_created_recently(), False)

現(xiàn)在我們擁有了4個(gè)測(cè)試塞祈,來(lái)保證was_created_recently()方法對(duì)于過(guò)去金刁、最近未來(lái)中的4種情況都返回正確的值议薪。你還可以繼續(xù)擴(kuò)展胀葱,直到你覺(jué)得完全沒(méi)有任何bug藏匿的可能性為止。

在實(shí)際的開(kāi)發(fā)中笙蒙,有些難纏的bug會(huì)把自己偽裝得非常的好抵屿,而不是像教程這樣明確的知道它就在那里。有了自動(dòng)化測(cè)試捅位,無(wú)論以后你的項(xiàng)目怎么變化轧葛、app交互多么的復(fù)雜搂抒,只要在測(cè)試中寫(xiě)好的邏輯就一定是符合預(yù)期的,而你所需要做的只是運(yùn)行一條測(cè)試指令而已尿扯。

雖然教程中僅使用了assertIs求晶,但實(shí)際上Django中的斷言有大概幾十種之多,比如assertEqual衷笋、assertContains等芳杏,并且還在不斷更新。詳見(jiàn)Python標(biāo)準(zhǔn)斷言Django擴(kuò)展斷言

測(cè)試視圖

上面的測(cè)試都是針對(duì)模型的辟宗。視圖該怎么測(cè)試爵赵?如何通過(guò)測(cè)試系統(tǒng)模擬出用戶(hù)的請(qǐng)求呢?

答案是TestCase類(lèi)提供了一個(gè)供測(cè)試使用的Client來(lái)模擬用戶(hù)通過(guò)請(qǐng)求和視圖層代碼的交互泊脐。

文章詳情視圖瀏覽量統(tǒng)計(jì)為例空幻,比較容易出現(xiàn)的潛在bug有:

  • 增加的瀏覽量未能正常保存進(jìn)數(shù)據(jù)庫(kù)(即每次請(qǐng)求則瀏覽量+1)
  • 增加瀏覽量的同時(shí),updated字段也錯(cuò)誤的一并更新

所以有針對(duì)的寫(xiě)2條測(cè)試容客。新寫(xiě)一個(gè)專(zhuān)門(mén)測(cè)試視圖的類(lèi)秕铛,與前面的測(cè)試模型的類(lèi)區(qū)分開(kāi):

article/tests.py

...
from time import sleep
from django.urls import reverse


class ArticlePostModelTests(TestCase):
    ...


class ArtitclePostViewTests(TestCase):

    def test_increase_views(self):
        # 請(qǐng)求詳情視圖時(shí),閱讀量 +1
        author = User(username='user4', password='test_password')
        author.save()
        article = ArticlePost(
            author=author,
            title='test4',
            body='test4',
            )
        article.save()
        self.assertIs(article.total_views, 0)

        url = reverse('article:article_detail', args=(article.id,))
        response = self.client.get(url)

        viewed_article = ArticlePost.objects.get(id=article.id)
        self.assertIs(viewed_article.total_views, 1)

    def test_increase_views_but_not_change_updated_field(self):
        # 請(qǐng)求詳情視圖時(shí)缩挑,不改變 updated 字段
        author = User(username='user5', password='test_password')
        author.save()
        article = ArticlePost(
            author=author,
            title='test5',
            body='test5',
            )
        article.save()

        sleep(0.5)

        url = reverse('article:article_detail', args=(article.id,))
        response = self.client.get(url)

        viewed_article = ArticlePost.objects.get(id=article.id)
        self.assertIs(viewed_article.updated - viewed_article.created < timezone.timedelta(seconds=0.1), True)

注意看代碼是如何與視圖層交互的:response = self.client.get(url)向視圖發(fā)起請(qǐng)求并獲得了響應(yīng)但两,剩下的就是從數(shù)據(jù)庫(kù)中取出更新后的數(shù)據(jù),并用斷言語(yǔ)句來(lái)判斷代碼是否符合預(yù)期了供置。

運(yùn)行測(cè)試:

(env) > python manage.py test

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
......
----------------------------------------------------------------------
Ran 6 tests in 0.617s

OK
Destroying test database for alias 'default'...

6條測(cè)試用例全部通過(guò)谨湘。

越多越好的測(cè)試

僅僅是app中的兩個(gè)非常小的功能,就已經(jīng)寫(xiě)了6條測(cè)試用例了士袄,并且還可以繼續(xù)擴(kuò)展。除此之外谎僻,其他的每個(gè)模型娄柳、視圖都可以擴(kuò)展出幾十甚至上百條測(cè)試,這樣下去代碼總量很快就要失去控制了艘绍,并且相對(duì)于業(yè)務(wù)代碼來(lái)說(shuō)赤拒,測(cè)試代碼顯得繁瑣且不夠優(yōu)雅。

但是沒(méi)關(guān)系诱鞠!就讓測(cè)試代碼繼續(xù)肆意增長(zhǎng)吧挎挖。大部分情況下,你寫(xiě)完一個(gè)測(cè)試之后就可以忘掉它了航夺。在你繼續(xù)開(kāi)發(fā)的過(guò)程中蕉朵,它會(huì)一直默默無(wú)聞地為你做貢獻(xiàn)的。最壞的情況是當(dāng)你繼續(xù)開(kāi)發(fā)的時(shí)候阳掐,發(fā)現(xiàn)之前的一些測(cè)試現(xiàn)在看來(lái)是多余的始衅。但是這也不是什么問(wèn)題冷蚂,多做些測(cè)試也不錯(cuò)。

深入代碼測(cè)試

在前面的測(cè)試中汛闸,我們已經(jīng)從模型層和視圖層的角度檢查了應(yīng)用的輸入輸出蝙茶,但是模板呢?雖然可以用assertInHTML诸老、assertJSONEqual等斷言大致檢查模板中的某些內(nèi)容隆夯,但更加近似于瀏覽器的檢查就要使用Selenium等測(cè)試工具(畢竟Django的重點(diǎn)是后端而不是前端)。

Selenium不僅可以測(cè)試 Django 框架里的代碼别伏,甚至還可以檢查 JavaScript代碼蹄衷。它假裝成是一個(gè)正在和你站點(diǎn)進(jìn)行交互的瀏覽器,就好像有個(gè)真人在訪問(wèn)網(wǎng)站一樣畸肆。Django 提供了LiveServerTestCase來(lái)和Selenium這樣的工具進(jìn)行交互宦芦。

關(guān)于測(cè)試的話題這里只是開(kāi)了個(gè)頭,讀者可以繼續(xù)閱讀下面的內(nèi)容進(jìn)一步了解:

總結(jié)

有一幫崇尚“測(cè)試驅(qū)動(dòng)”的開(kāi)發(fā)者轴脐,他們開(kāi)發(fā)時(shí)先寫(xiě)測(cè)試代碼调卑,然后才寫(xiě)業(yè)務(wù)代碼。而普通開(kāi)發(fā)者通常是先寫(xiě)業(yè)務(wù)代碼大咱,再寫(xiě)測(cè)試代碼恬涧,這也是沒(méi)問(wèn)題的。但如果你已經(jīng)寫(xiě)了很多業(yè)務(wù)代碼了碴巾,再回頭寫(xiě)測(cè)試確實(shí)有些無(wú)從下手溯捆,那么至少在以后寫(xiě)新功能時(shí),記得加上測(cè)試厦瓢。測(cè)試寫(xiě)得好不好提揍,甚至比功能本身更能看出編程水平。

測(cè)試可以讓代碼更加強(qiáng)壯煮仇。項(xiàng)目沒(méi)出bug時(shí)劳跃,皆大歡喜,有沒(méi)有測(cè)試都一樣浙垫;一旦出現(xiàn)難纏的bug刨仑,你就會(huì)無(wú)比想念一套完善的測(cè)試代碼了。

博主寫(xiě)自己的網(wǎng)站時(shí)就沒(méi)有對(duì)測(cè)試給與足夠的重視夹姥,回想起來(lái)走了很多彎路杉武。希望讀者以前車(chē)之鑒,培養(yǎng)良好的編程習(xí)慣辙售。


  • 有疑問(wèn)請(qǐng)?jiān)?a target="_blank">杜賽的個(gè)人網(wǎng)站留言轻抱,我會(huì)盡快回復(fù)。
  • 或Email私信我:dusaiphoto@foxmail.com
  • 項(xiàng)目完整代碼:Django_blog_tutorial
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末旦部,一起剝皮案震驚了整個(gè)濱河市十拣,隨后出現(xiàn)的幾起案子封拧,更是在濱河造成了極大的恐慌,老刑警劉巖夭问,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泽西,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡缰趋,警方通過(guò)查閱死者的電腦和手機(jī)捧杉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)秘血,“玉大人味抖,你說(shuō)我怎么就攤上這事』伊福” “怎么了仔涩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)粘舟。 經(jīng)常有香客問(wèn)我熔脂,道長(zhǎng),這世上最難降的妖魔是什么柑肴? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任霞揉,我火速辦了婚禮,結(jié)果婚禮上晰骑,老公的妹妹穿的比我還像新娘适秩。我一直安慰自己,他們只是感情好硕舆,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布秽荞。 她就那樣靜靜地躺著,像睡著了一般抚官。 火紅的嫁衣襯著肌膚如雪扬跋。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天耗式,我揣著相機(jī)與錄音胁住,去河邊找鬼趁猴。 笑死刊咳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的儡司。 我是一名探鬼主播娱挨,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼捕犬!你這毒婦竟也來(lái)了跷坝?” 一聲冷哼從身側(cè)響起酵镜,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎柴钻,沒(méi)想到半個(gè)月后淮韭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贴届,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年靠粪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片毫蚓。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡占键,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出元潘,到底是詐尸還是另有隱情畔乙,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布翩概,位于F島的核電站牲距,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏氮帐。R本人自食惡果不足惜嗅虏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望上沐。 院中可真熱鬧皮服,春花似錦、人聲如沸参咙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蕴侧。三九已至择同,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間净宵,已是汗流浹背敲才。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留择葡,地道東北人紧武。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像敏储,于是被迫代替她去往敵國(guó)和親阻星。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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