介绍

作为开发者我们可能都有过这样的经历:

  • Laravel v7 都已经发布了,而自己维护的项目仍然是公司祖传的 v5.3,迟迟不敢升级。
  • 修复了一个注册功能的 bug,结果把登录功能搞崩了,直到用户反馈才知道。
  • 新增功能或者修改代码都束手束脚,生怕对项目造成破坏性影响。

而这些困境很大部分的原因在于项目缺少完善的测试,无法保障项目的健壮,接下来我们就来分享我司经过数年的实践,锤炼出来的一套成熟的测试工艺。

利用 Laravel 提供的测试工具

Laravel 对请求进行了封装,我们可以非常方便地在测试中发起各种请求,比如说测试用户列表。 下面是 Laravel 自带的示例代码:

public function testBasicTest()
{$response = $this->get('/users');$response->assertStatus(200);
}

  • 在运行测试前需要创建数据表。
  • 需要填充一些测试数据,否则测试空的用户列表没有太大意义。
  • 希望能验证请求返回的 response 数据是否符合预期。

下面我们就来一一解决以上问题。

Migrations

Laravel 提供了 migration 来创建和更改数据表结构,在测试启动前可以先运行 migrate 命令。随着时间的推移,表结构变化积累得越多,migration 耗时就会越长。

Migration 对数据库做增量修改的做法并没有给测试带来任何好处,相反,在运行测试之前如果能对数据库进行全量构造,性能会提高很多。所以我们创建了自动化工具,对于每次添加新的 migration,都会自动生成全量的表结构描述文件用于测试。

Seeding

有了上一步的 migration 生成的数据表,我们还需要往数据表中插入测试数据,Laravel 再次贴心的提供了 seeding 功能,但是 seed 文件是通过手写的数组来存放数据,比如像下面这个样子:

<?phpuse IlluminateDatabaseSeeder;
use DB;class UsersTableSeeder extends Seeder
{public function run(){DB::table('users')->insert(['name' => 'baijunyao',]);}
}

我们的业务逻辑比较复杂,需要多套测试数据集,每套测试数据集中的数据又各有依赖,所以我们维护数套 YAML 格式存储的数据集,并实现了 YamlSeeder 来将 YAML 数据用于 seed 测试数据库。

users:- name: baijunyao

Assert Response

Laravel 提供了一系列的 assert 方法,但是一个一个的手动调用这些方法比较繁琐,手动硬编码 response 的数据就更加痛苦了。

public function testBasicTest()
{$response = $this->get('/users');$response->assertStatus(200);$response->assertJson([['name' => 'baijunyao',]]);
}

我们的方案是开发了一套自动 snapshot 测试工具。我们的测试用例有两种模式运行测试:创建 snapshot 和执行测试。前者可以自动捕捉所有 API response 并以 JSON 格式存储,后者则会将该次测试输出和 snapshot 进行比较以做断言。以 JSON 格式存在的 snapshot 结果集随代码一同 commit 到代码仓库中,可以方便地追踪每次的代码修改对 response 造成的影响。

{"status_code": 200,"headers": {"cache-control": ["no-cache, private"],"date": "Mon, 06-Jan-2020 00:00:00 GMT","content-type": ["application/json"],"x-ratelimit-limit": [60],"x-ratelimit-remaining": [59],"set-cookie": ["foo=bar; expires=Mon, 06-Jan-2020 01:00:00 GMT; max-age=3600; path=/; secure; httponly"],},"content": [{"name": "baijunyao"}]
}

这种比对整个 response 的方案中有一些细节需要注意,比如:

变量

有一些变量比如日期,可能造成每次的 response 都不一样,我们可以使用 Carbon 在测试模式中设置一个固定的当前日期。

Carbon::setTestNow(Carbon::create(2020, 1, 1, 0, 0, 0));

对于其他一些变量数据采用Mockery,无法 mock 的则忽略变量部分。

重置测试数据

因为新增数据的功能测试会真实的向数据库中插入一条数据,为了清理脏数据,Laravel 提供了 IlluminateFoundationTestingRefreshDatabase trait,它使用的是数据库的事务,存在的问题是数据虽然重置了,但是并没有重置自增型的主键,会造成每次运行测试时 id 不确定的问题。我们的解决方案是通过 DB 监听执行的 SQL,然后通过 TRUNCATE 来清理被污染的数据。

app('db')->listen(function ($query) {// ...
});

数据库

Laravel 默认是使用 SQLite 作为数据库,我们使用的则是 MySQL,在每次启动测试前创建一个临时数据库,测试结束后销毁这个临时的数据库,借助于 Docker 我们可以最大程度的保障测试环境跟生产环境的一致性。

结语

编写测试代码是一个必要而且有价值的工作内容,它可以让我们有底气的进行功能开发,快速的进行迭代,希望我们的测试方案对您有所帮助,围绕着测试我们开发了一系列的扩展包来简化我们编写测试代码的工作,如果对我们的测试方案有兴趣,欢迎留言,我们会逐步进行更深入更详细的讲解并开源这些工具。


请关注我们的微信公众号「RightCapital」

sqlite时间比较_一份经过时间检验的 Laravel PHPUnit 测试经验分享相关推荐

  1. 量化延时法时间测量_「交易技术前沿」交易系统低延时测试与分析

    本文选自 <交易技术前沿>总第三十三期文章(2018年12月) 证券期货行业测试中心(中金所) 魏畅 陈冬严 张鸿晔 摘要:订单延时(Latency)是衡量交易系统性能的重要指标.本文利用 ...

  2. 百度时间显示_文章的发布时间对百度优化网站重要吗

    文章的发布时间对百度优化网站重要吗?这个问题,相信很多初做网站优化的萌新朋友都会问到,以小匠个人的经历来分享这个问题的经验,小匠认为,文章的发布时间对优化网站是非常重要的,下面小匠将从实际经历来给大家 ...

  3. mysql查询今日没有时间字段_关于日期及时间字段的查询

    前言: 在项目开发中,一些业务表字段经常使用日期和时间类型,而且后续还会牵涉到这类字段的查询.关于日期及时间的查询等各类需求也很多,本篇文章简单讲讲日期及时间字段的规范化查询方法. 1.日期和时间类型 ...

  4. unity许可证不可用_不可思议之梦蝶从PC版移植到Nintendo Switch经验分享(上)

    本文将分享队友游戏制作人李喆Unite 2019上的技术演讲-<不可思议之梦蝶>从PC版移植到Nintendo Switch经验分享. 受篇幅限制,本次演讲将分享二篇内容.本文将分享:需要 ...

  5. 六星教育python怎么样_六星教育:Python怎么学?自学Python经验分享

    近些年,随着人工智能.大数据.自动化运维等行业的兴起,Python在编程方向上得天独厚的优势也凸显出来.随着Python工程师需求的日益增多,Python薪资也随之水涨船高.学习Python的同学越来 ...

  6. eui加载时间长_游戏加载时间越来越短了?背后藏着这些小心机

    在两大尚未发售的次世代主机中,或许你们还没决定好究竟是选择 PS5 还是 Xbox Series X,但毫无疑问次世代游戏表现已经震撼了所有人.更精美画面表现,更丰富的 HDR 效果,更快速的读取时间 ...

  7. 心跳超时时间设置_定时器实现之时间轮算法

    前言 在看这篇文章的时候对其中超时控制一块儿有点好奇.通过时间轮来控制超时?啥是时间轮?怎么控制的?文章会先介绍常见的计时超时处理,再引入时间轮介绍及 netty 在实现时的一些细节,最后总结下实现的 ...

  8. date日期 和 date时间拼接_函数周期表丨时间丨值丨DATE

    DATE函数 语法= DATE(<年>, <月>, <日>) DATE函数,属于"值函数"之一,返回某个具体的日期.用的频率不是特别的高,一般用 ...

  9. python指定时间执行程序_如何在特定时间执行程序

    我有一个程序需要每隔一定的时间执行.例如,我可能希望它每五分钟执行一次.我有几个与多个终端节点设备通信的协调器.下面的代码是关于协调员的.我需要它,以便如果interval设置为5,那么它将运行并记录 ...

最新文章

  1. Python学习笔记之基础(二)变量和类型
  2. linux相等路径,关于linux:如何检查Bash中两条路径是否相等?
  3. LA3266田忌赛马
  4. STM32F105使用固件库的时候串口乱码解决方法
  5. Memcached缓存在.Net 中的使用(memcacheddotnet)
  6. powerdesigner 反向工程 oracle,PowerDesigner oracle 反向工程到cdm文件
  7. 漫步微积分十四——增、减函数和极大、极小值
  8. Android基础知识学习
  9. 《敏捷软件开发过程及最佳实践》培训总结
  10. 数据可视化之MATPLOTLIB实战:PLT.POLAR()函数 绘制极线图 (转载)
  11. DOCKER-COMPOSE搭建MONGODB分片集群(单机版)
  12. Structure of a Task
  13. 如何下载Visual Studio Code及配置教程
  14. 提取unity3d游戏资源文件
  15. DBMS (数据库管理系统) 是什么
  16. Distral: Robust multitask reinforcement learning.
  17. Excle之说(2)-如何读“excel”
  18. 或且非 java_Java的运算符号(逻辑与、或、非、移位运算)
  19. python网课 知乎_如何看待风变编程的 Python 网课
  20. 深入支付宝支付扫描支付-跳转支付宝二维码页面支付与自定义生成二维码支付-2跳转固定的支付宝页面进行扫码支付

热门文章

  1. POJ3714 Raid 分治/K-D Tree
  2. 算法-字符串 循环左移
  3. 自定义cell的左侧滑动
  4. 事件总线帧---Otto
  5. C#正则表达式 — 正则表达式类
  6. 为什么使用接口编程{转载}
  7. vs.net 2005中引用webservice的简单方法
  8. 帆软报表,报错:sql注入攻击问题
  9. Xshell 鼠标选中 中断ctrl+c 问题
  10. sparkR介绍及安装