单元测试

请注意,这是一个选看章节,如果你原来一直在使用单元测试,那么这节你必不可错过。如果你原来没有做过单测,或者说极少做,我们强烈建议你,从此开始把单元测试作为一种习惯。

导语

在写这章之前,笔者一直很踌躇,因为我并没有多年的开发经验,甚至是一年都没有。换言之,我还没有一个良好的软件开发习惯,没有一个标准的开发约束,如果你和我一样,那么请你一定要仔细阅读本小节,并且开始尝试认真,仔细的做单测,它将会让你受益匪浅。

谈到单测,我想许多人和我一样一开始都比较抗拒,因为这是一种自我的否定,你默认自己写出来的代码是有问题的,于是你尝试用不同的方法,不同的角度去测试你代码的健壮性。于是不出所料,你的单测没有通过,或者部分通过,然后开始在代码中找 bug,边找边改。

没错,这看起来很烦,很浪费时间,甚至占据了开发的一半时间。但是人无完人,玉无完璧,没有人可以很自信的说,我的代码就是完美的,在辩证法中,有一种不断自我否定追求极致的言论。著名的曾国藩先生,被很多人评价为古今第一完人,那么他真的是完人吗?当然不是的,曾国藩不知道有多少的小毛病和错误,但他一直坚持自我完善,所以才能做到趋于极致,连毛主席都对他敬赞有加,甚至拿他和孔子相提并论。

所以,我想你已经大致明白了我的意思。单测真的很有必要,你可以在单测中找到自己的毛病,认识自己的不足,甚至在与其对抗时不断的提高自己,凝练自己思维,这将会让你的成长越发迅速。

准备

回到正题,koa 是一个轻量级的库,轻量到连单元测试都没有提供。那还能怎么办,当然是选择原谅它啊。koa 没有但是不代表不能做啊,在单测方面 Lin 选择整合 Jest。

在 starter 项目中,我们已经安装好了 Jest,你可以方便的直接使用。

测试

接下来让我们开始一个简单的测试吧。聪明的你,一定会发现我们项目的根目录下有一个tests 的目录,按照约定,请将专用于测试的文件全部置于此目录下。然后在此目录下的api目录下新建test1.test.js文件,并向其中加入以下内容:

  1. require("../helper/initial");
  2. const request = require("supertest");
  3. const { createApp } = require("../../app/app");
  4. const { db } = require("lin-mizar/lin/db");
  5. describe("test1.test.js", () => {
  6. // 必须,app示例
  7. let app;
  8. beforeAll(async () => {
  9. // 初始化app示例
  10. app = await createApp();
  11. });
  12. afterAll(() => {
  13. // 最后关闭数据库
  14. setTimeout(() => {
  15. db.close();
  16. }, 500);
  17. });
  18. });

接下来,我们书写测试函数:

  1. require("../helper/initial");
  2. const request = require("supertest");
  3. const { createApp } = require("../../app/app");
  4. const { db } = require("lin-mizar/lin/db");
  5. describe("test1.test.js", () => {
  6. // 必须,app示例
  7. let app;
  8. beforeAll(async () => {
  9. // 初始化app示例
  10. app = await createApp();
  11. });
  12. afterAll(() => {
  13. // 最后关闭数据库
  14. setTimeout(() => {
  15. db.close();
  16. }, 500);
  17. });
  18. // 测试 api 的函数
  19. // 测试 api的 URL 为 /cms/test/
  20. test("测试/cms/test/", async () => {
  21. const response = await request(app.callback()).get("/cms/test/");
  22. expect(response.status).toBe(200);
  23. expect(response.type).toMatch(/html/);
  24. });
  25. });

在这个测试函数中,我们测试了/cms/test/这个 API。

接下来,运行这个测试文件:

  1. npx jest tests/api/test1.test.js

如果一些顺利,它会给你一个 pass。

很好,一般情况下 http 请求偶尔会带 params,params 我们可以在请求路径中加入,如/cms/user/login?name=pedro,这样 name 就可以被解析成一个参数,参数值为pedro,对于数据传入我们可通过如下方式进行传入:

  1. test("测试/cms/user/register 输入不规范用户名", async () => {
  2. const response = await request(app.callback())
  3. .post("/cms/user/register")
  4. .send({
  5. nickname: "p",
  6. group_id: 1,
  7. password: "123456",
  8. confirm_password: "123456"
  9. });
  10. expect(response.status).toBe(400);
  11. expect(response.body).toHaveProperty("error_code", 10030);
  12. expect(response.type).toMatch(/json/);
  13. });

在 lin-cms-koa 的 starter 项目中,有诸多的测试示例,如果你对代码质量有要求,可以详细参考一下。

尾语

以上两个测试对于一般的项目 API 开发来说可能已经足够,但是对于其它类型的单测,笔者均未提到。这些测试大多异曲同工,也希望你能真正的去学习如何利用单测来提高代码质量,顺便凝练自己的思维,愿你在漫漫的成长路上珍惜每一个自我否定,自我沉淀的机会,与诸君共勉之。