强烈建议基于测试驱动开发。测试驱动开发,可以有效沉淀开发成果,当代码出现变更时也能尽快锁定潜在问题,从而显著提升代码的鲁棒性

EggBornJS在EggJS的基础上,提供了一个便捷的框架,可以很方便的实现基于测试驱动的开发

模块test-party

模块test-party是开箱即用的测试模块,提供大量的测试驱动开发指引。包含CabloyJS所有内置核心模块的测试用例,不仅可以验证CabloyJS架构本身的鲁棒性,也可以作为实际业务开发的代码范本

在本教程中,示范了以下几个环节的测试。您可以此为范例,不断扩充测试用例,尽可能多的提高代码覆盖率

  1. 用户登录
  2. 新建party
  3. 提交party(从草稿状态提交到正常状态
  4. 查询party
  5. 删除party

测试代码

src/suite-vendor/test-party/modules/test-party/backend/test/controller/test.test.js

  1. 1const { app, mockUrl, mockInfo, assert } = require('egg-born-mock')(__dirname);
  2. 2
  3. 3describe('action: atom: party', () => {
  4. 4 it('[atom]', async () => {
  5. 5 app.mockSession({});
  6. 6
  7. 7 // atomClass info
  8. 8 const atomClassModule = mockInfo().relativeName;
  9. 9 const atomClassName = 'party';
  10. 10
  11. 11 // login as root
  12. 12 await app.httpRequest().post(mockUrl('/a/authsimple/passport/a-authsimple/authsimple')).send({
  13. 13 data: {
  14. 14 auth: 'root',
  15. 15 password: '123456',
  16. 16 },
  17. 17 });
  18. 18
  19. 19 // create
  20. 20 let result = await app.httpRequest().post(mockUrl('/a/base/atom/create')).send({
  21. 21 atomClass: { module: atomClassModule, atomClassName },
  22. 22 });
  23. 23 assert(result.body.code === 0);
  24. 24 const keyDraft = result.body.data;
  25. 25
  26. 26 // submit
  27. 27 result = await app.httpRequest().post(mockUrl('/a/base/atom/writeSubmit')).send({
  28. 28 key: keyDraft,
  29. 29 item: {
  30. 30 atomName: 'party demo',
  31. 31 personCount: 3,
  32. 32 },
  33. 33 });
  34. 34 assert(result.body.code === 0);
  35. 35 const keyFormal = result.body.data.formal.key;
  36. 36
  37. 37 // read
  38. 38 result = await app.httpRequest().post(mockUrl('/a/base/atom/read')).send({
  39. 39 key: keyFormal,
  40. 40 });
  41. 41 assert(result.body.code === 0);
  42. 42
  43. 43 // delete
  44. 44 result = await app.httpRequest().post(mockUrl('/a/base/atom/delete')).send({
  45. 45 key: keyFormal,
  46. 46 });
  47. 47 assert(result.body.code === 0);
  48. 48
  49. 49 });
  50. 50});
名称 说明
mockUrl 用于构造完整的后端API接口地址
mockInfo 用于获取当前测试所属模块的基本信息。比如当前模块名是test-party,但为了提升代码的灵活性,可以通过mockInfo().relativeName获取
app.mockSession 用于模拟Session环境

执行测试

  1. 1$ npm run test:backend

测试覆盖率统计

  1. 1$ npm run cov:backend

Bean组件:version.mamager

测试用例执行之前,每个模块的Bean组件version.mamagertest方法都会自动执行

可以在这个阶段准备一些测试数据(如测试用的角色用户权限等等)

以模块test-party为例:

src/suite-vendor/test-party/modules/test-party/backend/src/bean/version.manager.js

  1. 1const VersionTestFn = require('./version/test.js');
  2. 2
  3. 3async test() {
  4. 4 const versionTest = new (VersionTestFn(this.ctx))();
  5. 5 await versionTest.run();
  6. 6}

app.meta.isTest, app.meta.isLocal

可以通过app.meta.isTestapp.meta.isLocal在合适的环境加载合适的代码逻辑

比如,模块test-party里面的资源,只允许在测试环境开发环境中生效,在生产环境无效,可以操作如下:

src/suite-vendor/test-party/modules/test-party/backend/src/models.js

  1. 1const party = require('./model/party.js');
  2. 2const partyType = require('./model/partyType.js');
  3. 3
  4. 4module.exports = app => {
  5. 5 const models = {
  6. 6 };
  7. 7 if (app.meta.isTest || app.meta.isLocal) {
  8. 8 Object.assign(models, {
  9. 9 party,
  10. 10 partyType,
  11. 11 });
  12. 12 }
  13. 13 return models;
  14. 14};