c++进化之路(一)

之前的主要经验是java/golang,虽然一直有长期写c/c++的希望,但一直没有计划付诸实践,主要还是工作机会和自身水平两方面。java/golang拥有作为高级语言的各种特征,让程序员的生产效率有提升,在屏蔽一些复杂度的同时,也增加了学习成本。

最近加入新团队,有机会长期base在c++上做一些技术工作,但技术内容和之前的很相似,有点像java到golang的迁移,但这次语言迁移的难度上会大一些,主要试试对于指针以及内存管理的理解问题。同时c++程序设计方式的对于初学者来说相比其他语言,有难度。经过近5年的工作,渐渐意识到,解决问题的思路和刚就业时还是有很多相同的地方,虽然经验丰富了许多,但仍旧有些稚嫩,不像之前学习高等数学,能有理有据的对答案进行推到,在最终结局问题之前会遇到很多阻碍比较难以克服。举最近处理一个问题的例子,记录下思路,供后续查看,并便于深思问题的处理方法。

我们的项目是基于brpc做的rpc服务,和大部分业务项目相似,依赖一些其他服务,类似redis·下游服务等,因为长期被线上core困扰,团队决定通过单元测试增加代码质量。我这边便开始尝试给手头的一个简单的项目进行单元测试的试点,该项目除了使用brpc框架进行服务提供之外,针对调度类型这种非纯api式的服务并没有规范的编写方法,这块儿和redis·mysql相似,采用多线程模型,分离各种功能到相应的线程中,类似置加载·周期触发某事件这种。下面是每次做决策的细节。

当前主要问题是,对于各依赖服务,采用类似static类和单例模型这种方法,这给单元测试造成比较大的困扰,很多待测方法都饮用依赖服务,实际上大部分测试是在测试针对依赖服务的不同响应,方法本身是否能作出正确回应。==java web服务一般会使用spring,而spring不仅封装了对外提供服务的能力,也提供了帮助应用方管理类的的容器,实现依赖注入。让每一个被测类,变成一个pojo class,这样方便单元测试。==这样就引入第一个工作,就是要将封装依赖服务接口的类改造为平常类,即没有static和单例模式。按照这个思路,找到fruit,google开源的一个依赖注入工具库。在改造过程中,遇到的主要问题是fruit推荐项目是面向接口编程,需要找到和具体类结合的方法,幸运的是教程bindInstance这个明确的方法。另一个问题是有些对象,想要控制全局只有一个。这个暂且用static关键字来限制。以上,完成依赖注入的改造。

接下来,要处理的是单元测试的问题,使用的的google的gtest,gtest内部内置了gmock的工具库。因为大部分方法都存在依赖服务对象,这样这里的主要工作便是gmock的引入。当前项目采用blade构建和编译,集成gtest到blade是第一工作,并针对简单的工具方法(没有依赖对象)进行了测试,顺利完成。接下来,准备使用gmock,参考教程编写之后一直提示Undefined referrence to xxx,指向的是gmock内部的方法,虽然我引入gmock/gmock.h,但仍旧不起作用,google半天,大部分都说的是原理层面的东西,能找到header文件,但是并没有实现与之对应,在blade的BUILD文件中我尝试了各种配置,这段时间我基本上就是试图暴力破解当前问题,很多配置并不是推导出的决策:

  • 重新用最新的blade进行编译。
  • test binary配置和服务binary配置保持一致。
  • 更改代码。(基本是胡乱的试验)

上述的第三点在更改include位置的时候,解决掉了,最开始连gtest中工具方法都找不到的情况,这在之前的java/golang是没有的东西,include的先后顺序是有要求的,这块是c++带给我的一次不小的问题。花了半天时间,最终依靠幸运值搞定。

面对gmock,也同样花了半天时间,虽然gmock被内置到gtest中,但blade在加载依赖时配置的目录是gtest,虽然lib目录存在gmock的静态库文件,但未必会自动加载进来。最后我分离出gmock目录,并在BUILD中增加配置,才柳暗花明。

库引入成功后的问题就相对有理有据一些,例如gmock要求被测试类面像接口编程,我们mock的才能作为构造函数参数传入,根据实际情况,代码改动量太大,而且业务场景没必要用接口(用接口只是图增代码量),决定找到mock具体类的方法,幸运的是教程也有描述,就是利用模版类的方法。

最终,陆陆续续花了1周的时间,才将整个技术链条串起来,虽然也是利用一些时间片进行调研,时间也是花费的过长了。上面的描述随简短,但基本能体现一个平庸程序员是怎么解决问题的。

c++进化之路(一)
Share this