自动化测试(12) | Selenium Java 测试方案

自动化测试方案

自动化测试方案

自动化测试框架图

Selenium自动化测试方案.png
  1. 测试用例使用单元测试框架
  2. 测试业务使用 Page-Object 设计模式
  3. 测试数据使用 数据驱动的方式 进行传入
    1. flat-file
      1. csv
      2. xml
      3. txt
      4. ...
    2. DataBase
      1. MySQL
      2. SQL Server
      3. MongoDB
  4. 底层封装 WebDriver

测试用例组织与执行

  • 测试集合 test suite
    • 测试用例的集合
      • 多个测试用例的类
      • 测试用例类的方法
  • 测试运行 运行 test suite

TestNG

  • testng.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
    <suite name="Default Suite">
        <test name="SolutionSelenium">
            <classes>
                <class name="cases.LoginTests">
                    <methods>
                          <!-- 添加指定的测试 -->
                        <include name="testLoginByCsv"/>
                          <include name="testLoginByCsv2"/>
                          <include name="testLoginByCsv3"/>
                    </methods>
                </class> <!-- cases.LoginTests -->
                <class name="cases.AdminTests">
                    <methods>
                          <!-- 去除指定的测试 -->
                        <exclude name="testAddMember"/>
                    </methods>
                </class>
            </classes>
        </test> <!-- SolutionSelenium -->
    </suite> <!-- Default Suite -->
    
    
    • 指定测试的类
    • 指定测试的类中的方法
      • include: 一个个方法包含进来
      • exclude: 去除指定的方法
  • Main.java

    public class Main {
        public static void main(String[] args) {
            TestNG test = new TestNG();
            List<String> suites = new ArrayList<>();
            suites.add("testng.xml");
            test.setTestSuites(suites);
            test.run();
        }
    }
    
  • 测试报告

    • TestNG 自带的测试报告 xml / html

    • ReportNG 测试报告的插件,停止开发和支持。

      Report detail page.
    • ExtentReport 测试报告

      • TestNG有默认的测试报告生成器 Listener
        • 使用 ExtentReport 重写一个 Listener
        • 让 TestNG 使用我们写好的 Listener 生成报告
    • 步骤

      • 引入 ExtentReport 到 pom.xml

        <dependency>
          <groupId>com.relevantcodes</groupId>
          <artifactId>extentreports</artifactId>
          <version>2.41.2</version>
        </dependency>
        
      • 编写 Listener : ExtentReporterNgListener

        @Override
        public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
          Date date = new Date();
          SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd_HHmmss");
          String time = formatter.format(date);
        
          String reportName = String.format("ExtentReportTestNG_%s.html", time);
          // 创建报告
          // NetworkMode.OFFLINE 支持断网查看报告
          this.extent = new ExtentReports(
            outputDirectory + File.separator + reportName,
            true, NetworkMode.OFFLINE);
        
          for (ISuite suite : suites) {
            Map<String, ISuiteResult> result = suite.getResults();
        
            for (ISuiteResult r : result.values()) {
              ITestContext context = r.getTestContext();
              // 创建测试节点
              buildTestNodes(context.getPassedTests(), LogStatus.PASS);
              buildTestNodes(context.getFailedTests(), LogStatus.FAIL);
              buildTestNodes(context.getSkippedTests(), LogStatus.SKIP);
            }
          }
          extent.flush();
          extent.close();
        }
        
      • 修改 testng.xml

        <suite /> 中添加 <listener/>

        <listeners>
          <listener class-name="runner.ExtentReporterNgListener"/>
        </listeners>
        
      • 正常运行测试

unittest

  • 添加 test suite

    suite = unittest.TestSuite()
    suite.addTest(LoginTests("test_login_by_csv"))
    suite.addTest(LoginTests("test_login_by_csv2"))
    suite.addTest(AdminTests("test_add_member_by_csv"))
    
    • 这里可以配置测试,到外部文件,数据库中等。

      测试类,测试方法
      LoginTests,test_login_by_csv
      LoginTests,test_login_by_csv2
      AdminTests,test_add_member_by_csv
      

  • 实例化 test runner

    # 测试报告的文件
    test_time = time.strftime("%Y%m%d_%H%M%S", time.localtime())
    report_file = open("reports\\ranzhi_automate_report_%s.html" % test_time,
                       mode="wb")
    runner = HtmlTestRunner(stream=report_file,
                            verbosity=2,
                            title="然之系统自动化测试报告",
                            description="具体测试报告内容如下: ")
    

    HtmlTestRunner : 第三方测试报告运行器

  • 用 test runner 去执行测试,产生报告。

    runner.run(suite)
    

项目讨论

  • 项目中符合自动化测试的部分有哪些?(目标和范围 scope, 准入准出标准)
    • 稳定的需求点、变动较少的页面
    • 每日构建后的测试验证 daily build
    • 比较频繁的回归测试
    • 需要在多平台上运行的相同测试案例、组合遍历型的测试、大量的重复任务
  • 自动化用例在整个项目的测试用例的覆盖率
    • 一般的要求 50% +
    • 重点的要求 80% +
    • 根据项目的具体要求,变动特别大的项目需要额外单独考虑覆盖率

团队建设

建立自动化测试的组,理想状态下有4个人员,测试开发、中高级自动化测试工程师、2个初级自动化工程师;非理想的情况下,可能只有一个人。

  • 测试开发:

    基础答案:自动化框架的建设,确定自动化框架的设计模式、第三方代码工具的封装、中间公共模块的设计和调用、测试用例、测试套件的管理和执行、测试报告和测试结果的输出(文件输出和邮件通知)

    可选高级:如果可能的话,需要搭建持续集成服务器(CI,Continuous Integration Server)的环境,进行持续交付和自动化的冒烟测试等。

    培训的任务,需要将设计的框架以及封装的驱动,对其他成员进行培训。

    有自动化方案的实施经验、有开发背景、以及持续集成的背景等。

  • 中高级自动化测试工程师:

    配合测试开发人员,实施测试框架的建设。主要负责中间公共模块的实现和实例化等,以及部分高难度和流程复杂的自动化用例脚本编写和调试等工作。

    有参与过自动化方案的建设、脚本编写经验丰富、会代码调试、懂Web测试等。

  • 初级自动化测试工程师:
    根据中间公共模块的设计,进行实例化公共模块、方法组合,实现自动化用例脚本的编写。
    有计算机编程思维、有代码经验、可以读懂脚本和HTML等。
  • 若只有我一个人:
    首先实现自动化用例的维护和执行。在这个基础上不断的抽取实现公共模块的设计以及测试报告的生成等工作。通过经验的积累,以及后续人员的补充,早日做好自动化框架的建设工作。

技术方案

Selenium WebDriver、Python(unittest) Java(JUnit、TestNG)、CI Server

技术方案:
选择Python + Selenium 或者 Java + Selenium 的技术方案。

首先技术工具是免费的,Python的工具用PyCharm社区版,Selenium的WebDriver是开源工具。利用比较简洁的Python语言进行自动化测试,对于人员的学习成本来讲比较实用,学习时间短,有优势。

另外Python自带的unittest单元测试框架可以很方便的实现自动化用例的设计和执行以及自动化用例套件的管理等任务。Python是纯面向对象的语言,后续也可以过渡到Java + Selenium进行更加丰富的自动化测试。
此外,可以选择Jenkins作为持续集成服务器,配合Python+Selenium的方案进行自动化冒烟测试。

硬件:
硬件的要求不高,主要需要独立的测试环境。另外测试人员用的电脑最好是Windows桌面操作系统,需要安装Firefox浏览器,避免47.0的最新版本。测试人员最好也使用Chrome浏览器辅助进行Web元素的定位。

持续集成的概念

持续集成的定义

持续集成,Continuous integration ,简称CI。

随着软件开发复杂度的不断提高,团队开发成员间如何更好地协同工作以确保软件开发的质量已经慢慢成为开发过程中不可回避的问题。尤其是近些年来,敏捷(Agile) 在软件工程领域越来越红火,如何能再不断变化的需求中快速适应和保证软件的质量也显得尤其的重要。

持续集成正是针对这一类问题的一种软件开发实践。首先我们看一下,敏捷教父 Martin Fowler 对持续集成的定义:

Martin Fowler:Continuous Integration is a software development practice where members of a team integrate their work frequently, usually each person integrates at least daily - leading to multiple integrations per day. Each integration is verified by an automated build (including test) to detect integration errors as quickly as possible. Many teams find that this approach leads to significantly reduced integration problems and allows a team to develop cohesive software more rapidly.

具体定义:持续集成式一种软件开发实践。它倡导团队的成员必须经常的集成他们的工作,通常至少每天一次甚至更多次集成。每次集成都需要通过自动化的构建(包括编译代码、构建应用、部署程序以及自动化测试)来验证,从而尽早尽快的发现集成中的错误。大量的团队利用这样的方式来更快的开发内聚的软件。大大减少此过程中的集成问题。

具体的流程图如下:

Paste_Image.png

持续集成强调开发人员提交了新代码之后,立刻进行构建、(单元、自动化)测试。根据测试结果,我们可以确定新代码和原有代码能否正确地集成在一起。

首先,解释下集成。我们所有项目的代码都是托管在SVN服务器上。每个项目都要有若干个单元测试,并有一个所谓集成测试。所谓集成测试就是把所有的单元测试跑一遍以及其它一些能自动完成的测试。只有在本地电脑上通过了集成测试的代码才能上传到SVN服务器上,保证上传的代码没有问题。所以,集成指集成测试。

再说持续。不言而喻,就是指长期的对项目代码进行集成测试。既然是长期,那肯定是自动执行的,否则,人工执行则没有保证,而且耗人力。对此,我们有一台服务器,它会定期的从SVN中检出代码,并编译,然后跑集成测试。每次集成测试结果都会记录在案。完成这方面工作的就是下面要介绍的Jenkins软件。当然,它的功能远不止这些。在我们的项目中,执行这个工作的周期是1天。也就是,服务器每1天都会准时地对SVN服务器上的最新代码自动进行一次集成测试。

Paste_Image.png

持续集成的特点

  • 它是一个自动化的周期性的集成测试过程,从检出代码、编译构建、运行测试、结果记录、测试统计等都是自动完成的,无需人工干预;
  • 需要有专门的集成服务器来执行集成构建;
  • 需要有代码托管工具支持;

持续集成的作用

  • 代码库存越是积压,就越得不到生产检验,积压越多,代码间交叉感染的概率越大,下个发布(release)的复杂度和风险越高,持续集成可以保证团队开发人员提交代码的质量,减轻了软件发布时的压力;
  • 持续集成中的任何一个环节都是自动完成的,无需太多的人工干预,有利于减少重复过程以节省时间、费用和工作量;
  • 及早的发现代码中的问题,及早解决,代码越早推送(PUSH)出去,用户能越早用到,快就是商业价值;

举个例子,你家装修厨房,其中一项是铺地砖,边角地砖要切割大小。如果一次全切割完再铺上去,发现尺寸有误的话浪费和返工时间就大了,不如切一块铺一块。装修厨房有很多部分,每个部分都有检测手段,如地砖铺完了要测试漏水与否,线路铺完了要通电测试电路通顺,水管装好了也要测试冷水热水。如果全部装完了再测,出现问题可能会互相影响,比如电路不行可能要把地砖给挖开……。全部装修完了,你去验收,发现地砖颜色不合意,水池太小,灶台位置不对,返工吗?所以不如没完成一部分,你就去用一下试用验收……这就是持续集成。在敏捷的思想里面,不光强调上面的过程,还需要强调自动化这个过程。过技术手段自动化这三个工作。加快交付速度,自动化测试是一个重要的组成部分。

Jenkins

Jenkins介绍

Jenkins,原名Hudson,2011年改为现在的名字,它 是一个开源的实现持续集成的软件工具。官方网站:http://jenkins-ci.org/

Hudson是在2004年的夏天由Sun公司开发的(就是开发Java的那家),2005年2月开源并发布了第一个版本。Hudson发布的时候CruiseControl是CI界的老大哥,但是很快,在大约2007年的时候Hudson已经超越CruiseControl。2008年5月的JavaOne大会上,Hudson获得了开发解决方案类的Duke's Choice奖项。从此,小弟翻身做大哥,Hudson成为CI的代名词。

2009年6月,Oracle收购Sun。2010年9月,Oracle注册了Hudson的商标,然后就没有然后了。

mv -f Hudson Jenkins

Jenkins 能实施监控集成中存在的错误,提供详细的日志文件和提醒功能,还能用图表的形式形象地展示项目构建的趋势和稳定性。

Jenkins特点

  • 易安装:仅仅一个 java -jar jenkins.war,从官网下载该文件后,直接运行,无需额外的安装,更无需安装数据库;
  • 易配置:提供友好的GUI配置界面;
  • 变更支持:Jenkins能从代码仓库(git/Subversion/CVS)中获取并产生代码更新列表并输出到编译输出信息中;
  • 支持永久链接:用户是通过web来访问Jenkins的,而这些web页面的链接地址都是永久链接地址,因此,你可以在各种文档中直接使用该链接;
  • 集成E-Mail/RSS/IM:当完成一次集成时,可通过这些工具实时告诉你集成结果(据我所知,构建一次集成需要花费一定时间,有了这个功能,你就可以在等待结果过程中,干别的事情);
  • JUnit/TestNG测试报告:也就是用以图表等形式提供详细的测试报表功能;
  • 支持分布式构建:Jenkins可以把集成构建等工作分发到多台计算机中完成;
  • 文件日志信息:Jenkins会保存哪次集成构建产生了哪些jars文件,哪一次集成构建使用了哪个版本的jars文件等构建记录;
  • 支持第三方插件:使得 Jenkins 变得越来越强大;

Jenkins的安装

需要从官网下载Jenkins的文件,在本地安装Java(jdk)的环境以后,直接执行以下语句进行安装:

java -jar jenkins.war

注意上述操作,一定是在Java环境已经配置好的情况下进行。输出如下:

Microsoft Windows [Version 10.0.10532]
(c) 2015 Microsoft Corporation. All rights reserved.

C:\Users\erha>java -version
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)

C:\Users\erha>java -jar d:\Jenkins\jenkins.war
Running from: D:\Jenkins\jenkins.war
webroot: $user.home/.jenkins
Jun 19, 2016 12:52:18 AM winstone.Logger logInternal
INFO: Beginning extraction from war file
Jun 19, 2016 12:52:29 AM org.eclipse.jetty.util.log.JavaUtilLog info
INFO: jetty-winstone-2.9
Jun 19, 2016 12:52:33 AM org.eclipse.jetty.util.log.JavaUtilLog info
INFO: NO JSP Support for , did not find org.apache.jasper.servlet.JspServlet
Jenkins home directory: C:\Users\erha\.jenkins found at: $user.home/.jenkins
Jun 19, 2016 12:52:35 AM org.eclipse.jetty.util.log.JavaUtilLog info
INFO: Started SelectChannelConnector@0.0.0.0:8080
Jun 19, 2016 12:52:35 AM winstone.Logger logInternal
INFO: Winstone Servlet Engine v2.0 running: controlPort=disabled
Jun 19, 2016 12:52:36 AM jenkins.InitReactorRunner$1 onAttained
INFO: Started initialization
Jun 19, 2016 12:53:11 AM jenkins.InitReactorRunner$1 onAttained
INFO: Listed all plugins
Jun 19, 2016 12:53:12 AM jenkins.InitReactorRunner$1 onAttained
INFO: Prepared all plugins
Jun 19, 2016 12:53:12 AM jenkins.InitReactorRunner$1 onAttained
INFO: Started all plugins
Jun 19, 2016 12:53:12 AM jenkins.InitReactorRunner$1 onAttained
INFO: Augmented all extensions
Jun 19, 2016 12:53:15 AM jenkins.InitReactorRunner$1 onAttained
INFO: Loaded all jobs
Jun 19, 2016 12:53:25 AM hudson.model.AsyncPeriodicWork$1 run
INFO: Started Download metadata
Jun 19, 2016 12:53:26 AM org.jenkinsci.main.modules.sshd.SSHD start
INFO: Started SSHD at port 10556
Jun 19, 2016 12:53:26 AM jenkins.InitReactorRunner$1 onAttained
INFO: Completed initialization
Jun 19, 2016 12:53:26 AM jenkins.InitReactorRunner$1 onAttained
INFO: Started initialization
Jun 19, 2016 12:53:26 AM jenkins.InitReactorRunner$1 onAttained
INFO: Listed all plugins
Jun 19, 2016 12:53:26 AM jenkins.InitReactorRunner$1 onAttained
INFO: Prepared all plugins
Jun 19, 2016 12:53:26 AM jenkins.InitReactorRunner$1 onAttained
INFO: Started all plugins
Jun 19, 2016 12:53:26 AM jenkins.InitReactorRunner$1 onAttained
INFO: Augmented all extensions
Jun 19, 2016 12:53:26 AM jenkins.InitReactorRunner$1 onAttained
INFO: Loaded all jobs
Jun 19, 2016 12:53:26 AM jenkins.InitReactorRunner$1 onAttained
INFO: Completed initialization
Jun 19, 2016 12:53:26 AM hudson.WebAppMain$3 run
INFO: Jenkins is fully up and running
Jun 19, 2016 12:53:32 AM hudson.model.UpdateSite updateData
INFO: Obtained the latest update center data file for UpdateSource default
Jun 19, 2016 12:53:33 AM hudson.model.DownloadService$Downloadable load
INFO: Obtained the updated data file for hudson.tasks.Maven.MavenInstaller
Jun 19, 2016 12:53:34 AM hudson.model.DownloadService$Downloadable load
INFO: Obtained the updated data file for hudson.tasks.Ant.AntInstaller
Jun 19, 2016 12:53:37 AM hudson.model.DownloadService$Downloadable load
INFO: Obtained the updated data file for hudson.tools.JDKInstaller
Jun 19, 2016 12:53:37 AM hudson.model.AsyncPeriodicWork$1 run
INFO: Finished Download metadata. 12,471 ms

随后访问http://localhost:8080即可

最后,将Jenkins安装成Windows服务启动

在Jenkins的主页中选择 Manager Jenkins

[图片上传失败...(image-3cb7ce-1516465996163)]

接下来选择 Install as Windows Service

[图片上传失败...(image-63c5a2-1516465996164)]

在Installation Directory中选择jenkin的安装路径。这里会默认产生,直接点击Install就可以了。

[图片上传失败...(image-69b3c8-1516465996164)]

随后我们点击Yes,然后可以看到Windows服务中添加了Jenkins的服务,并已经设置为自动和启动状态。

[图片上传失败...(image-faf91-1516465996164)]

持续集成的实践

创建Jenkins Job

Jenkins提供了四种类型的Job:

  1. 构建一个自由风格的软件项目:这个是Jenkins的主要功能,可以构建一个你自己需要的项目。
  2. 构建一个maven项目:这是基于maven构建的项目,整个过程将会基于你的.pom文件进行构建,大大减轻构建的配置
  3. 构建一个多配置项目:这种项目适用多配置的项目,比如多个平台定制的构建,多个测试环境的部署构建等。
  4. 监控一个外部的任务:这种事允许你记录和执行不在Jenkins中的Job,这些Job可以运行在远程的主机上,Jenkins通过远程自动构建,作为一个控制面板而存在。

运行Jenkins Job

运行Job只需要在页面的左侧选中已经列出的项目,进行操作就可以了。选择立即构建,便可以进行自动构建的工作了。

定时构建 Job

目前有一个每日构建的概念。

Daily Build,每日构建。需要Jenkins在每日固定的时间进行代码自动构建、集成和测试的工作。那么需要定制执行时间。Jenkins的自动构建定制时间是遵循cron语法的。具体来说,每一行包括了5个用白空格或者Tab隔开的字段,分别是:MINUTE HOUR DOM MONTH DOW。具体的格式我们参考下图

字段 说明 示例
MINUTE Minutes within the hour (0–59) 30
HOUR The hour of the day (0–23) 17
DOM The day of the month (1–31) 1
MONTH The month (1–12) 5
DOW The day of the week (0–7) where 0 and 7 are Sunday. 5

然后每个格式,都可以由*-/,4种字符组成:

  • *代表所有可能的值
  • -代表整数之间的范围
  • /代表指定的时间的间隔频率
  • ,代表指定的列表范围

命令的格式参考和示例

分钟 小时 月份 星期 执行的命令
* 16 1,10,20 * * python xxx 每个月的1,10,20日的16:00执行 python xxx 命令格式:* 16 1,10,20 * *
* 16 * * 1-5 python yyy 每个周的周一到周五的16:00执行 python yyy 命令格式:* 16 * * 1-5
30 17 * * 1,5 python zzz 每个周的周一和周五的17:30执行 python zzz 命令格式:30 17 * * 1,5

ps: 注意时间是伦敦时间

至此,我们将会对Jenkins有一个非常范和初步的了解过程。

示例:

1. 创建SVN仓库 
    https://172.31.95.168/svn/DemoRepo/
    ciuser / ciuser
2. 从SVN仓库签出文件到CI Server
3. 构建 build
4. 部署 
    4.1 备份目标文件夹 
        若有ranzhi_bak 需要先删除
        rd /s /q c:\xampp\htdocs\ranzhi_bak
        把 htdoc的ranzhi改成 ranzhi_bak
        xcopy c:\xampp\htdocs\ranzhi c:\xampp\htdocs\ranzhi_bak\ /s /e /y
        rd /s /q c:\xampp\htdocs\ranzhi
    4.2 复制构建的版本到目标文件夹
        把workspace的 ranzhi 复制到 htdocs下面
        xcopy "%WORKSPACE%\ranzhi" c:\xampp\htdocs\ranzhi\ /s /e /y
    4.3 恢复配置文件
        复制 ranzhi_bak\config\my.php 到 ranzhi\config\my.php
        xcopy c:\xampp\htdocs\ranzhi_bak\config\my.php c:\xampp\htdocs\ranzhi\config\ /e
5. 自动化测试
    python D:\Git\Coding\BWFTraining\3.03_Selenium\codes\weekend2demo\ranzhi_test_runner.py
    

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容