db和mailer初始化逻辑和依赖单独放在handler.js文件中,这个文件代码修改频率更低,这样修改后的代码就满足了准则一。
业务逻辑也不依赖任何外部服务,在单元测试时既可以使用真实的db和mailer服务,也可以模拟db和mailer服务,使单元测试简单,使得代码更易于扩展。
想将代码迁移到其他FaaS平台,不用修改业务逻辑,只需要提供handler.js。
提供一个handler.js使其适用于新的FaaS平台从而避免云厂商绑定。
需要对User类编写单元测试
首先需要选择一个测试框架,比如jest,jest可以零配置上手使用。内置的mock功能提供了完善的测试覆盖率报告等。
可以使用npm install -d jest来安装,在package.json中添加一个jest命令即可。
为了方便的管理所有测试用例,可以创建一个__test__目录,在里面新建名为users.test.js文件,用来编写Users类的测试用例。
jest默认会将test目录或包含test关键词文件当中代码用作单元测试。
https://gitee.com/pingfanrenbiji/serverless-class/tree/master/08/unit-te
Users类主要提供save方法,save方法的功能把用户信息存入数据库,然后给用户发送一封邮件,那么代码运行中可能存在这几种情况。
- • 用户信息写入数据库成功发送邮件成功
- • 用户信息写入数据库成功,发送邮件失败
- • 用户信息写入数据库失败
这个时候可能感觉有到有点困难了,写数据库或发邮件都依赖远程服务,而且还要那么多异常情况来考虑,怎么进行测试呢?
对于save方法来说本质上不需要关注远程服务,只需要考虑分支逻辑的正确性。
这个地方可以对db和mailer模拟,模拟db和mailer的各自异常情况,然后观察save方法的执行结果是否正确。
FaaS提供mock功能,可以让我们对类进行模拟。
对db.saveUser和mailer.sendWelcomeEmail函数进行模拟
所以只需要对这两个函数进行模拟,当db.saveUser执行成功,发送邮件的返回值为true。
针对save方法编写第一个测试用例
覆盖率100%表示所有代码都经过了测试。
模拟业务变动的情况比如回调函数不再返回userId
这个时候就需要考虑这个变更对上下游的影响,避免造成线上业务风险,在确认没有风险之后,再去修改测试用例。
小结
- • 为了更好的管理代码,建议单元测试目录结构和业务代码结构保持一致
- • 将业务代码和依赖的云服务分离开来,这样才能方便测试
- • 单元测试速度足够快,因为单元测试是用来辅助开发的,运行若是非常繁琐,运行速度慢会影响开发效率
- • 单个测试要小于200毫秒,整个系统的测试要小于10分钟
- • 要隔离一切外部调用,比如不能读磁盘、不能有网络调用,不能写数据库,不能依赖环境变量,不能依赖系统时间
- • 必要时需要对外部进行模拟,用来确保单元测试不会对外部环境有所影响。由于模拟外部aip可能导致内部代码行为发生改变,所以要按照最新的外部api描述进行模拟
- • 单一职责,一个测试用例只用来验证一个行为
- • 单元测试代码是最好的文档和描述,因此单元测试需要明确代码的意图。好的单元测试应该是自描述的能对代码进行解释说明
- • 单元测试并不是测试的全部,这是用来保证单个功能、组件的正确性,在Serverless中依旧使用集成测试来验证所有的组件集成在一起时运行是否正常。在设计和编写业务代码的时候需要考虑代码是否有利于测试,在这个基础之上,业务代码的单元测试和传统的应用单元测试的方法是互通的。
- • Serverless应用由于其分布式、依赖云服务、事件驱动等特性导致编写单元测试很困难,为了方便编写单元测试,需要将业务逻辑和依赖的云服务分离开来
- • 编写单元测试时,需要考虑速度、隔离性、单一职责等因素避免单元测试称为开发的负担