如何写出具有高鲁棒性的代码-

Posted by AlstonWilliams on February 17, 2019

马不停蹄的做了两周的Android开发.好久没做了,连最基本的内容基本上都已经忘记了.

在做Android开发的过程中,给我们的后台服务提交了大量的issue,这迫使我思考到底怎样才能写出健壮的代码.

在这片文章中,我将会介绍,我想到的方法.

TDD(测试驱动开发)

尽管这个在业界似乎还不是很流行,但是它对于保证代码的健壮性很重要.我们开发人员中的很多人,在开发完成后,不会写测试用例来对代码进行完整的测试.比如说,对于一个修改用户信息的功能,可能只会测试正确的输入.而对于那些异常情况,比如,我们知道,修改用户信息的接口,一定是需要传一个用户的uuid来识别是修改的哪位用户的信息,而用户(这里我们称前台开发人员为用户)在调用的时候,可能并没有传用户的uuid的情况,并不会测试到.对于这种情况,不够健壮的代码会给出500,服务器错误,而健壮的代码会给你一个错误信息,告诉你缺少了必要的参数.在我们的服务中,格式为:

{
	"code": "error_code",
    "data": "error_message"
}

但是如果我们在开发之前,确定下来我们的模块需要拥有的接口,以及接口需要的参数,进一步确定可能会有什么错误发生,并提前写好相应的测试脚本.在开发完成后,执行一下测试脚本,便能发现代码中的错误,代码还会不健壮吗?

还是上面那个例子,修改用户的信息的接口,假设我们允许修改的用户信息有:

  • 用户昵称(长度最长为20个字节,即最长20个英文单词,10个汉字)(必须传入)
  • 用户性别(1代表男,2代表女)(必须传入)
  • 用户出生日期(格式为YYYY-MM-DD)(必须传入)
  • 用户简介(长度最长为40个字节,即最长40个英文单词,20个汉字)(必须传入)

我们可以确定可能有下面的异常情况:

  • 用户没有传入表明用户身份的uuid
  • 用户传入了一个错误的uuid,即不存在的用户
  • 用户没有传入必要的参数,比如缺少用户昵称,用户性别等
  • 用户传了不合法的参数,比如传入了用户的密码
  • 传入的用户昵称为null
  • 传入的用户昵称为”“(即空字符串)
  • 传入的用户昵称长度大于20字节
  • 传入的用户昵称为不支持的字符编码
  • 传入的用户性别格式不正确,比如传入’woman’
  • 传入的用户性别的取值不正确,比如传入了’3’
  • 传入的用户出生日期格式不正确,比如传入的格式为’YYYY-MM-DD hh:mm:ss’
  • 传入的用户出生日期取值不正确,比如现在是2017年,他传入的用户年份是2018或者1800,传入的月份是13,等
  • 传入的用户简介为null
  • 传入的用户简介为”“(即空字符串)
  • 传入的用户简介长度大于40字节
  • 传入的用户简介为不支持的字符编码
  • 用户传入了正确的数据但是返回的结果不正确

我们根据上面可能的异常情况以及正确输入写一个测试脚本,在Java中,对于Restful接口,我们使用Rest Assured库.开发完成之后,只需执行一下测试脚本,就能发现程序中存在哪些错误.

做回归测试

我们在修复一个issue,或者增加新的接口的时候,并不能确保我们的修改对其他接口没有影响.所以,我们还需要做回归测试.如果上面写了TDD的测试脚本,那这里我们只需要在这个脚本中新增新接口的测试用例,再执行一遍测试就Ok了.如果没有上面的那个脚本,这里你也很可能就会因为嫌麻烦而不做.

我们项目组中的成员,很多次就是因为没有执行回归测试,而导致新接口能用而旧接口失效的问题.

将生产环境下数据库需要的约束同样加在开发环境下

数据库约束其实也是提高我们的代码健壮性的一个好帮手.比如,在新增用户的粉丝接口中(用户和粉丝的关系在一张单独的表中),我们需要用户传入的粉丝id是一个存在的用户的uuid.如果不用约束,我们需要在代码中先遍历数据库来查询用户是否存在,而如果我们直接使用外键约束,让粉丝id是用户id的外键,那么我们只需要查看数据库是否返回违反外键约束的错误就能确定用户是否存在了.

在修改用户信息的那个例子中,如果你没有先写测试脚本,忘了判断用户昵称是否为空,而你数据库中,用户昵称字段有非空约束.通过传入空昵称时这个非空约束报的错误,你就能知道需要先判断用户昵称是否为空.

永远不要认为你依赖的东西会正常工作

作为一个后台微服务,我们一定会用到数据库,会用到缓存.如果你假设数据库不会宕机,并没有处理数据库宕机的代码,那你的代码永远都不够健壮.一旦数据库宕机,前台就会返回大量的500,有心人就会有可乘之机了.

做压力测试

有的错误,如果你不做压力测试,是察觉不到的.比如,不正确的并发处理,死锁等问题.对于一个点赞接口来说,如果你用postman来进行测试,点一次结果正确,点两次结果正确,但是你并发的点一万次,结果可能就会因为并发处理不对而是9990了.

如果你没用数据库连接池,网络带宽足够大,并发执行十万次数据库写操作,数据库可能就会因此宕机(至少会有明显的网路延时),这时候如果你的微服务如果对外提供正确的错误信息,那恭喜,你的代码足够健壮了.