美文网首页
如何编写可测试的代码

如何编写可测试的代码

作者: wayyyy | 来源:发表于2022-08-07 00:24 被阅读0次

    编写可测试的代码可能比编写单元测试本身更加重要,可测试的代码简单来说就是指可以很容易的为其编写单元测试代码。

    参数最小依赖原则
    剔除干扰因素
    func judgeRate() int {
       now := time.Now()
       switch hour := now.Hour(); {
       case hour >= 8 && hour < 20:
           return 10
       case hour >= 20 && hour <= 23:
          return 1
       }
       return -1
    }
    

    这个函数内部使用了time.Now()来获取系统的当前时间作为判断的依据,看起来很合理。

    但是这个函数现在隐式包含了一个不确定因素——时间。在不同的时刻我们调用这个函数都可能会得到不一样的结果。想象一下,我们该如何为这个函数编写单元测试呢?

    如果不修改系统时间,那么我们就无法为这个函数编写单元测试,这个函数成了“不可测试的代码”(当然可以使用打桩工具对time.Now进行打桩,但那不是本文要强调的重点)。

    接下来我们该如何改造它?

    我们通过为函数传参数的方式传入需要判断的时刻,具体实现如下。

    // judgeRateByTime 报警速率决策函数
    func judgeRateByTime(now time.Time) int {
       switch hour := now.Hour(); {
       case hour >= 8 && hour < 20:
           return 10
       case hour >= 20 && hour <= 23:
            return 1
       }
       return -1
    }
    

    其他类似的还需要注意有:时间、随机数、并发性、基础设施、现存数据、持久化、网络。

    接口抽象进行解耦
    依赖注入代替隐式依赖

    在应用程序中使用全局变量的方式引入日志库或数据库连接实例。

    var log = logrus.New()
    
    type App struct{}
    
    func (a *App) Start() {
       log.Info("app start ...")
    }
    
    func (a *app) Start() {
       a.Logger.Info("app start ...")
       // ...
    }
    
    func main() {
       app := &App{}
       app.Start()
    }
    

    上面的代码中 App 中通过引用全局变量的方式将依赖项硬编码到代码中,这种情况下我们在编写单元测试时如何 mock log 变量呢?

    此外这样的代码还存在一个更严重的问题——它与具体的日志库程序强耦合。当我们后续因为某些原因需要更换另一个日志库时,我们该如何修改代码呢?

    我们应该将依赖项解耦出来,并且将依赖注入到我们的 App 实例中,而不是在其内部隐式调用全局变量。

    type App struct {
       Logger
    }
    
    func (a *App) Start() {
       a.Logger.Info("app start ...")
       // ...
    }
    
    // NewApp 构造函数,将依赖项注入
    func NewApp(lg Logger) *App {
       return &App{
          Logger: lg, // 使用传入的依赖项完成初始化
       }
    }
    

    依赖注入就是指在创建组件(Go 中的 struct)的时候接收它的依赖项,而不是它的初始化代码中引用外部或自行创建依赖项。


    参考资料:
    1、https://mp.weixin.qq.com/s/hzVIMDhPQXtWoBIj0Aueug

    相关文章

      网友评论

          本文标题:如何编写可测试的代码

          本文链接:https://www.haomeiwen.com/subject/bxnvwrtx.html