测试驱动开发(TDD)—— 数据库查询篇

在用 TDD 方式进行编程了一段时间后,我想应该分享一些使用心得体会。

查询

数据库查询,是我们从数据库中筛选出一些符合特征记录的标准。这就相当于,我们搭垂直电梯,在告知电梯我们要前往哪个楼层后,它就会将我们载到对应的楼层。但是同样的,我们其实也应该给一些反面的例子,比如超重了的话,它就不会正确的工作,甚至会出现意外的情况,这就很像生产事故了。

那么对于 TDD 来说,当我们校验这个查询是否有效的时候,我们应该添加一些反面的例子。否则你是不会留意到 select * fromselect id from where 1 = 1 的区别。所以基本上利用 TDD 开发模式的话,都是首先提出一个简单的实例,再提出一个反例,再通过反例来完善查询本身。

就比如说我们希望获取一个文章列表。我们先添加一篇文章:

INSERT INTO posts (id) VALUES (1);

同样,从最简单的查询开始,我们利用 PDO 或者 ORM 编写查询:

App\Posts::all();

在测试中,你可以验证这个查询是否确实加载了这篇文章。这时我往数据库中添加另一条记录,以证明查询能够返回多个对象(即没有 limit 或者任何其他的条件)。

INSERT INTO posts (id) VALUES (1);
INSERT INTO posts (id) VALUES (2);

实现第一个业务

对于这个业务来说,我们需要一些发布的文章,然而我们尚未考虑这个的实现,所以为了证明这条查询并未实现要求,我们添加一个反例,一篇未发布的文章:

-- 发布的文章
INSERT INTO posts (id, status) VALUES (1, 'published');
INSERT INTO posts (id, status) VALUES (2, 'published');

-- 草稿文章
INSERT INTO posts (id, status) VALUES (3, 'draft');

第一次 Red

这个时候再次运行测试,会发现这次是失败的,因为它返回来了一个草稿状态的文章。这个就是 TDD 的 Red 阶段。

接着修改我们的查询,给它添加 where

App\Posts::where('status', 'published')->get();

重新运行测试,再次进入 Green 阶段。

循序渐进

对于一个信息流来说,我们往往希望文章是根据发布时间逆序排列的,所以添加个发布时间。

-- 发布的文章
INSERT INTO posts (id, status, published_at) VALUES (1, 'published', '2019-09-02');
INSERT INTO posts (id, status, published_at) VALUES (2, 'published', '2019-09-03');

-- 草稿文章
INSERT INTO posts (id, status, published_at) VALUES (3, 'draft', '');

很显然,运行测试必然是失败的,所以我们修改查询:

App\Posts::where('status', 'published')
    ->orderBy('published_at', 'desc')
    ->get();

如此重复, 对于每个新的需求,首先提出一个反例推翻现阶段所实现的,从而促使整个业务开发是遵循 Green - Red - (Refactor) 的 TDD 循环。

繁琐吗?

这种开发模式繁琐吗?很繁琐,笔者一开始也很不适应,但是它能很有效地帮助我们建立一个安全可靠的查询,因为这个查询是你一步一步罗列下来的。

就像单元测试一样,断言每个结果,确保每个功能能正确的输出。

有用吗?

笔者运维过十年前的代码,当一个复杂的业务逻辑,你不知道为什么这样查询的时候,你也就无法确保在修改查询后,会引发另一种问题,当然这个也经常为什么出现生产事故的原因。

同样,TDD 的过程后,其实你有一个很好的起点可以进行修改,清楚地了解其中的内容以及反例。

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!