1.go sqlmocks的源码使用
2.一文让你快速上手 Mockito 单元测试框架!
3.用PowerMock写Java单元/集成测试
4.Dubbo源码解析:网络通信
5.单元测试实践
6.mock平台的解析实现原理是什么?
go sqlmocks的使用
本文主要介绍如何使用 DATA-DOG/go-sqlmock 来模拟数据库操作,解决使用 beego ORM 进行 `InsertOrUpdate` 操作时遇到的源码 bug。当在开发过程中遇到数据库操作相关的解析问题,特别是源码遇到与主键冲突的情况,可以通过模拟数据库返回结果来快速定位问题,解析loupan网站源码进而解决问题。源码
案例情景如下:有一个 `TExchangeInfo` 结构体,解析实例化后填充数据,源码然后执行 `InsertOrUpdate` 操作,解析当数据存在时,源码使用更新,解析当数据不存在时才插入。源码
使用 `sqlmock` 的解析方法相对简单,它通过 `sqlmock.New()` 返回一个标准的源码 `sql.DB` 结构体实例指针和一个 `sqlmock.Sqlmock` 结构体实例。通过 `*sql.DB` 可以传递给 ORM 进行数据库交互。
例如,使用 `beego ORM` 时,可以通过 `orm.NewOrmWithDB` 方法实例化并指定连接句柄。但在使用过程中,可能会遇到 `panic` 错误,这是因为 `sqlmock.Sqlmock` 需要在模拟测试中指定期望执行的查询语句及其返回结果。
在 `mock` 测试中,需要添加期望执行的查询语句和假定的返回结果。例如,在 `beego ORM` 启动时,会先执行 `SELECT TIMEDIFF...` 和 `SELECT ENGINE...` 两个语句,因此在期望中也需要包含这两个语句。
在 `mock` 测试后,可以打印出实际执行的 SQL 语句,以便分析问题。在分析输出语句时,发现 `beego ORM` 使用的是数据库自身的 `insert or update` 功能,但语句中没有包含主键的痕迹。
问题在于 `beego ORM` 在执行过程中过滤掉了主键,可能是因为在处理含有 `auto` 属性的字段时,忽略了主键的番茄直播APP源码处理。这可能是因为 `auto` 属性是 ORM 层的标记,用于指示字段应进行自动增长,而不是数据库层面的主键标记。
在源码中,发现问题出现在 `github.com/astaxie/beego@v1..2/orm/db_mysql.go` 文件中的第 行代码,以及 `github.com/astaxie/beego@v1..2/orm/db.go` 文件中的第 行代码。通过断点调试,发现当字段的 `tag` 包含 `auto` 属性时,ORM 会跳过主键的处理。
解决方法是去除字段的 `auto` 标记,确保 ORM 正确处理主键。在实际开发中,应使用 `pk` 标记来明确指定字段为数据库的主键。
经过与开发者沟通得知,开发者在创建数据库交互使用的数据结构体时,习惯在主键字段上添加 `auto` 标记,以表示主键自增。但实际上,`auto` 标记只用于 ORM 层的指示,而非数据库层面的主键标识。正确的做法是使用 `pk` 标记来明确指定主键。
通过去除 `auto` 标记,并使用 `pk` 标记正确指定主键,问题得到解决。这表明在使用 ORM 时,明确理解标记的含义及其在不同层面上的用途至关重要。
一文让你快速上手 Mockito 单元测试框架!
快速掌握 Mockito 单元测试框架!
在编程中,单元测试是检验代码模块独立性的关键。 Mockito,作为Java测试的得力助手,尤其在处理依赖对象测试时更为便捷。常规的Junit测试中,当测试类依赖其他类时,创建这些依赖对象会变得繁琐,易语言caffe源码而Mockito则提供模拟功能,让开发者聚焦于测试本身。
使用Mockito,首先要引入框架依赖。Maven项目中,添加如下依赖;Gradle项目则需类似操作。创建Mock对象有两种常见方式:一是通过Mockito.mock静态方法,如mock(List.class);二是使用@Mock注解,配合MockitoAnnotations.initMocks或@ExtendWith注解,如创建mock的List对象。
验证性测试是Mockito的核心功能。Mockito.verify允许检查方法调用次数,如验证mockList.size方法被调用一次,或指定调用次数。方法调用顺序通过inOrder进行验证。Mockito还可以模拟异常,比如定义get方法抛出异常。
Mockito在Spring框架集成也很方便,通过@MockBean将mock对象注入Spring容器,替换真实bean,如测试用户服务调用用户仓库的情况。
Mockito的工作原理在于方法拦截和上下文管理。通过Mockito.mock创建mock对象,when方法模拟方法行为,如when(mockTarget.foo("mghio")).thenReturn(expectedResult)。Mockito的核心逻辑涉及Byte Buddy和Objenesis库。
要自己实现一个类似Mockito的框架,需要理解方法拦截和动态生成mock对象。通过分析Mockito源码,我们可以构建一个简单的Mock框架,如imock,完成创建mock对象和行为定义。
总结,虽然Mockito提供了基础功能,但深入了解其高级用法还需查阅官方文档。记住,400套程序源码单元测试应重点针对核心业务和依赖部分,而非全面覆盖。通过本文,你已经对Mockito有了初步了解,开始你的单元测试之旅吧!
用PowerMock写Java单元/集成测试
在重构代码时添加单元测试能确保重构的正确性。研究了如何使用PowerMock进行Java单元和集成测试。PowerMock是对Mockito的改进,支持protected和static方法的模拟,但不支持private方法。配置PowerMock需要引入特定依赖库,用于集成测试。对于测试结构,约定使用src目录,将测试代码与源代码分开,并根据单元测试和集成测试的特点命名目录和文件。测试函数命名应能体现测试目的。使用不同注解区分单元测试和集成测试,优化启动时间。为避免测试间的相互影响,单元测试应仅关注单个函数的逻辑,而集成测试应覆盖业务流程的多个关键节点,尽量少地进行模块模拟。开始编写测试时,应遵循上述指导原则,如模拟外部依赖、处理数据库操作、测试异常和函数调用次数、静态方法和抽象类测试、接口模拟等。集成测试与单元测试类似,但考虑了更复杂的真实执行环境。集成测试中的数据库操作是否模拟取决于团队偏好和测试效率。在编写测试代码时,需注意函数逻辑的完整覆盖,同时简化数据准备和清理过程,分类信息破解源码以提高测试效率和可维护性。
Dubbo源码解析:网络通信
<dubbo源码解析:深入理解网络通信
在之前的章节中,我们已经了解了消费者如何通过服务发现和负载均衡机制找到提供者并进行远程调用。本章将重点解析网络通信的实现细节。
网络通信主要在Dubbo的Remoting模块中进行,涉及多种通信协议,包括dubbo协议、RMI、Hessian、HTTP、WebService、Thrift、REST、gRPC、Memcached和Redis等。每个协议都有其特定的优缺点,如Dubbo协议适用于高并发场景,而RMI则使用标准JDK序列化。
Dubbo的序列化机制支持多种方式,如Hessian2、Kryo、FST等。近年来,高效序列化技术如Kryo和FST的出现,可提升性能,只需在配置中简单添加即可优化。
关于数据格式和粘包拆包问题,Dubbo采用私有RPC协议,消息头存储元信息,如魔法数和数据类型,消息体则包含调用信息。消费者发送请求时,会通过MockClusterInvoker封装服务降级逻辑,然后通过序列化转换为网络可传输的数据格式。
服务提供方接收请求时,首先对数据包进行解码,确认其格式正确性,然后调用服务逻辑。提供方返回调用结果时,同样经过序列化和编码,最后通过NettyChannel发送给消费者。
在心跳检测方面,Dubbo采用双向心跳机制,客户端和服务端定期发送心跳请求以维持连接。此外,还通过定时任务处理重连和断连,确保连接的稳定性和可靠性。
总的来说,Dubbo的网络通信模块精细且灵活,通过多种协议和优化技术确保服务调用的高效和可靠性。
单元测试实践
非常有意思的一段话:
我们用得最多的基本单元测试框架是junit和testng,下面对这两个工具做个对比。
通过上面的对比可以看出,TestNG作为Java项目的单元测试框架是更有优势的,TestNG在参数化测试、依赖测试、套件测试、分组测试、并发测试等方面都比Junit4强,同时,TestNG涵盖了JUnit4的全部功能。
所以下面的案例说明都是基于TestNG来写的。
为方便对后面内容的理解,先写一个单元测试:
这是一个验证rsa加解密功能的单元测试。
TestMain是抽象出来,用于启动spring容器以及支持testng用例自动注入bean,因为启动spring容器总是很耗时的,如果我们的测试用例用不到依赖的spring bean,最好不雅启动spring容器,TestMain源码:
这就是TestMain最好放在和工程Application类所在包相同路径下的原因,比如我的示例中TestMain和BoardServerApplication都在相同包路径下:com.allawn.athletic.board.server。
检查TestNG插件是否存在
插件搜索“Coverage”
在我们的pom文件下加如下plugin配置:
idea插件自带,带搜索插件“PIT mutation testing”,但不建议用,很难调通!
本地开发环境
IDE自动创建单元测试的方法(也可手动完成):
创建后的单元测试在Maven工程的test目录下生成测试类:
注意:如果之前没有test目录,则需要手动创建一下:
然后再把目录设置为test目录。设置方法:file -> Project Structure -> Modules
运行单元测试
创建单元测试
Maven执行的相关命令:
如果单元测试不通过,出现如下:
两种方式皆可运行。
运行不通过则则会有提示
要通过maven运行单元测试,要保证pom配置没有跳过单元测试,检查设置如下:
IDEA可以直接生成覆盖率报告,导出来的覆盖率报告长这样:
点击index.html即可看报告内容:
变异测试,英文Mutation Testing,是使用变异器 (切换数学运算符,更改返回类型,删除调用等)将代码修改为不同的变异(基于变异器创建新代码),并检查单元测试是否失败。好的单元测试应该使所有突变都失败(杀死)。
主要列出testng的测试方法,junit的测试方法请另行百度。
异常测试是指在单元测试中应该要抛出什么异常是合理的,可以检测我们方法中指定跑出的异常,类似这种:
如果我们有时候不想测试某些方法的单元测试,那么我们可以指定这些具体的单元测试跳过不执行,testng和junit4都支持忽略测试,testng通过@Test(enabled=false)跳过。
指定某个单元测试方法最长执行时间,如果超时了就算失败,testng中的timeout单位是毫秒。
套件测试是指把多个单元测试组合成一个模块,然后一起运行,在套件定义中还可以通过定义组,针对相同组名的单元测试统一运行。
为方便我们模拟单元测试的传参,testng提供了@DataProvider注解,我们可以在单元测试内设置多种参数值,单元测试会依次把入参都跑一遍。被@DataProvider修饰的方法,返回值是数组形式。
依赖测试是指测试的方法是有依赖的,在执行的测试之前需要执行的另一测试。如果依赖的测试出现错误,所有的子测试都被忽略,且不会被标记为失败。
TestNG支持通过多个线程并发调用一个测试接口来实现性能测试,invocationCount表示方法调用的次数,threadPoolSize表示并发线程数量,timeOut即是每次调用最大耗时时间。
通过多线程并行调用多个测试方法,在我们套件/组测试的时候,如果使用并行测试,可以大大减少测试运行时间。
Mock的使用场景:
Mockito有比较简洁的API,简单易学,可读性强。从Mockito2开始,Mockito支持了很多新特性以及新注解(所以依赖mockito2.x以上版本的需要java8及以上jdk方可),使用很便捷,spring-boot-starter-test包默认内置mockito,鉴于维护性和语言新特性的支持,个人建议使用Mockito作为单元测试的mock工具。
在有需要的地方进行mock,否则走真实方法调用。
除了采用静态方法spy以外,还可以通过采用注解的方式:
结果示例:
除了@Spy注解需要如此设置,@Mock、@Captor、@InjectMocks等注解也需要。
对整个class进行mock
结果:
Junit5使用手册: junit.org/junit5/docs/c...
在Junit5中要使用Mockito,需要单独引入mockito-junit-jupiter依赖包,通过在单元测试类上加
@ExtendWith(MockitoExtension.class)
实现构建一个mock运行容器。
在有需要的地方进行mock,否则走真实方法调用。
除了采用静态方法spy以外,还可以通过采用注解的方式:
结果示例:
结果:
mock平台的实现原理是什么?
AREX 是一款基于真实请求与数据的自动化回归测试平台,利用 Java Agent 字节码注入技术,实现自动录制、自动回放、自动比对,为接口回归测试提供便利。AREX Mock 功能强大,支持各种主流技术框架的自动数据采集和 Mock,精准还原生产执行时的数据环境,且不会产生脏数据。
AREX 实现流量回放时自动 Mock 数据的原理包括录制和回放两个阶段。在录制阶段,当函数被调用时,将对应的请求参数和返回结果保存下来,用于后面流量回放使用。在回放阶段,用之前采集的数据自动实现函数 Mock,避免实际数据库操作。
以MyBatis3 Query为例,AREX会在BaseExecutor类的query方法中自动添加相应代码来实现功能。在执行数据采集时,会将数据库访问的结果、核心参数等信息存入AREX数据库中。当把录制的请求再次发送给对应服务时,AREX将其视为回放,直接返回之前录制下来的结果。
对于非幂等的函数,每次调用结果可能受到外部环境影响,AREX提供配置动态类机制实现数据的采集和回放时的 Mock 功能。具体配置在Setting子菜单的Record配置项中完成。
AREX平台提供全面文档、官网、GitHub源码和官方QQ交流群等资源,为用户提供详细指导和交流平台。
三方数据的mock环境搭建
为什么需要使用Mock环境
在CTU和数据产品的测试中,需要MockServer来模拟第三方的API接口,来返回指定的信息。目前主要有如下应用场景:
模拟DX数据平台服务。在测试CTU,数据平台SDK,第三方客户接入集成时,可模拟数据服务API,搭建轻量化的测试环境。
模拟第三方数据平台服务。在测试CTU或者我们的数据产品时,会经常使用到第三方数据服务。为节省成本和搭建可控的测试环境,需要模拟第三方数据服务的API。
根据以上的使用场景描述,MockServer需要满足如下需求(后续有其他需求继续添加):
根据不同的API地址选择不同的返回数据配置。
同样的API地址,根据请求Body中的不同内容返回不同数据。
能够获得请求中内容并在结果中返回。
支持/dreamhead/moco.git
然后在下载后的仓库目录中找到moco/README.md文件,找到最新版本jar包的下载地址。
下载地址为:/github/dreamhead/moco-runner/0..0/moco-runner-0..0-standalone.jar
依赖环境moco依赖于JAVA环境,因此需要安装(Centos7):\yuminstalljava\yuminstalljava-1.8.0-openjdk-devel
配置并实现所需功能moco的配置文件均使用json格式。
第三方数据平台服务moco支持指定全局配置文件,从而不同的URI请求可以获得不同的response。
全局配置文件配置全局配置例子如下:
其中,context指定URI,include指定对应的URI请求所采用的配置文件。
[{ "context":"/antifraud","include":"/home/alu/param/antifraud.json"},{ "context":"/nameCarValidate","include":"/home/alu/param/nameCarValidate.json"}][灵性编程]GO的依赖注入AND自动生成代码
依赖
总结下先有的获取对象依赖方式
比较原始的New,全局global保存
基于反射读取对象的依赖,程序启动时由DI库实例化(代表作dig等)
基于反射读取对象的依赖,编译前生成完整构建函数(代表作wire等)
第一种:最方便,直接快捷,大量依赖时候,但是因为是手动的,容易出现实例顺序非预期,不方便自动测试,mock等。
第二种:因为是启动时反射获取依赖的,需要定义额外的函数给DI系统解析,例如一个结构的注入必须要要额外的代码,非常麻烦,不建议使用
//提供者err:=c.Provide(func(conn*sql.DB)(*UserGateway,*CommentGateway,error){ //...})iferr!=nil{ //...}//使用者err:=c.Invoke(func(l*log.Logger){ //...})iferr!=nil{ //...}第三种,同样是基于反射,所以依然需要一个额外函数(只有配置信息)提供反射信息,生成同名函数,便捷度基本和手动New一致,wire由Google开源
funcInitializeNewGormProvider()*Gorm{ wire.Build(NewGormProvider,InitializeNewConfProvider)returnnil}我的方案原理和wire一样,根据配置信息生成自动构建函数,但是不基于反射,因为反射需要程序是完整的,编译后才读取信息,相对慢,需要每个目录改完手动执行wire.命令(每个目录每次花费1秒等)。
先看一个场景,数据库服务是依赖配置服务,从结构体就能看出来,不需要funcInitializeNewGormProvider()*Gorm{ }函数反射,未了更加准确(防止注入了不需要的内容)添加一个taginject:""和@Bean注解
//@BeantypeGormstruct{ conf*Conf`inject:""`}所以,注入其实是可以直接基于源码的信息都能实现的。
我只要实现一个go代码解析工具,就能生成和wire工具生成相同的代码,因为go源码的关键字和结构实在是太简单了,没有多少语法糖,做一下分词再按语法规则读取源码信息,工具实现比较容易。工具使用php实现(公司都是mac,php环境mac电脑自带,方便使用模版生成go代码)/go-home-admin/home-toolset-php重要是php解析很快,整个项目生成一次都是一秒内
ORM生成代码编写工具后,也可以生成其他辅助代码,例如原始结构,添加@Orm后,自动根据字段信息生成通用代码
//@OrmtypeGormstruct{ Iduint`json:"id"`UserNamestring`json:"user_name"`}逻辑就可以直接使用
u:=&UsersTable{ }data:=u.WhereUserName("test").And(func(table*UsersTable){ table.WhereId(1).OrWhereId(2)}).Or(func(table*UsersTable){ table.WhereId(2).Or(func(table*UsersTable){ table.WhereId(1)})}).Find()//select*formuserswhereuser_name=?and(id=?orid=?)or(id=?or(id=?))utils.Dump(data)作者:程序狗著作权归作者所有。