Fabric Chaincode调试 —— 开发者模式和单元测试
在fabric开发中,chaincode的测试是一个令人比较头疼的问题,一是由于实际情况中chaincode中的存储和查询是依赖于peer节点上的状态数据库的,所以无法在本地直接测试;二是由于chaincode是运行于容器中的,这导致我们很难获取在代码中打印的日志。
如果直接在实际开发环境中测试chaincode就更麻烦了,每一次调试都需要重启整个网络(有可能还是多机部署的),并且要创建和加入通道,安装以及实例化链码,这严重影响了测试的效率。下面介绍两种测试链码的手段,一种是开发者 (dev) 模式,在本地单机搭建一个简单的网络来进行测试;另一种是单元测试 (UT),可以无需启动节点环境,自动化测试所有接口。
开发者模式
环境分析
使用开发者调试环境,需要先下载fabric-samples,置于$GOPATH/src下。开发者调试目录位于:
fabric-samples/chaincode-docker-devmode
首先分析一下目录中的 docker-compose-simple.yaml 文件:
该网络中包含1个orderer节点,1个peer节点,1个chaincode容器(负责运行我们要测试的链码),1个cli容器(负责发送请求来测试链码)。
有两点需要注意的:
- 在cli容器的
command项中可以看见,启动后会自动执行当前目录下的script.sh脚本,该脚本会自动创建名为myc的通道,并且将节点加入。所以我们只需要安装和实例化链码即可。 - 在chaincode容器的
volumes中可以看见这样一条映射:- ./../chaincode:/opt/gopath/src/chaincode
说明
fabric-samples/chaincode目录会映射到容器内部,这也是我们待测试链码需要放置的地方。为了方便管理,我们可以在该目录下为每个链码再分配一个目录,然后把要测试的链码放在其中。(当然也可以直接修改映射指向自己chaincode的实际路径)。
测试过程
这里在以最简单的sacc.go为例,该链码只涉及到简单的存储(set)和查询(get)功能。整个过程需要启动三个终端:
终端一:启动网络
首先进入开发者模式目录:
cd fabric-samples/chaincode-docker-devmode
启动网络:
docker-compose -f docker-compose-simple.yaml up
当看到Going to wait for newer blocks时表示启动成功,此时网络中存在四个容器(1 orderer,1 peer, 1 chaincode, 1 cli),创建了通道myc并将peer成功加入。
终端二:编译链码
进入chaincode容器
docker exec -it chaincode bash
编译想要测试的chaincode:
cd sacc
go build
成功执行后单当前目录下会出现生成的可执行文件。此时需要启动这个可执行文件:
CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=mycc:0 ./sacc
注:这里有个不解的小问题,官网教程中的端口是peer:7051,并且当前peer确实也在监听7051,但是写成7051就会报错:Error starting SimpleAsset chaincode: error sending chaincode REGISTER。
当出现starting up ...的提示就说明链码启动成功了,在这个终端二里可以输出chaincode中的日志(比如通过fmt.Print()打印的内容)。
终端三:在cli中测试链码
进入cli容器:
docker exec -it cli bash
安装和实例化链码(实例化设置了a的初始值10):
peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v 0
peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C myc
进行测试:
调用set()接口将a的值设置为20:
peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc
调用get()接口查询a的值,发现a的值已经更新为20,测试完毕。
peer chaincode query -n mycc -c '{"Args":["get","a"]}' -C myc
在开发者环境中加入couchdb
如果实际开发的链码中使用了couchdb提供的富查询,则需要在测试环境中加入couchdb容器。
只需要对docker-compose-simple.yaml文件进行修改即可:
首先在文件中添加couchdb段的配置:
couchdb:
container_name: couchdb
image: hyperledger/fabric-couchdb
environment:
- COUCHDB_USER=
- COUCHDB_PASSWORD=
ports:
- 5984:5984
networks:
- default
在peer的environment部分添加:
environment:
- CORE_LEDGER_STATE_STATEDATABASE=CouchDB
- CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984
- CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=
- CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=
在peer的depends_on部分添加:
depends_on:
- couchdb
执行docker-compose命令后就可以启动couchdb容器,同时在浏览器中输入地址http://localhost:5984/_utils还可以进入couchdb的web端管理界面,更清晰的看到存入的数据,从而方便配合我们进行测试。
单元测试
单元测试 (UT) 可以提高调试的效率和我们代码的质量。fabric中提供了一个MockStub类用于单元测试。
单元测试
单元测试不需要启动任何网络节点,通过我们的测试文件就可以在本地对链码中的接口进行调用测试。
其原理就是在MockStub类中维护一个map[string][]byte来模拟key-val的状态数据库,链码调用的PutStat() 和 GetStat() 其实是作用于内存中的map。
MockStub主要提供两个函数来模拟背书节点对链码的调用:MockInit()和MockInvoke(),分别调用Init和Invoke接口。接收的参数均为类型为string的uuid(随便设置即可),以及一个二维byte数组(用于测试的提供参数)。
单元测试的要求:
- 需要导入
testing包 - 单元测试文件以
_test.go结尾 - 测试用例的函数必须以
Test开头
单元测试的例子
下面是对sacc.go的单元测试例子,由于该代码较简单,这里就将几个接口的测试写在一个case里。创建sacc_test.go文件,测试用例如下:
func TestFunc(t *testing.T) {
cc := new(SimpleAsset) // 创建Chaincode对象
stub := shim.NewMockStub("sacc", cc) // 创建MockStub对象
// 调用Init接口,将a的值设为90
stub.MockInit("1", [][]byte{[]byte("a"), []byte("90")})
// 调用get接口查询a的值
res := stub.MockInvoke("1", [][]byte{[]byte("get"), []byte("a")})
fmt.Println("The value of a is", string(res.Payload))
// 调用set接口设置a为100
stub.MockInvoke("1", [][]byte{[]byte("set"), []byte("a"), []byte("100")})
// 再次查询a的值
res = stub.MockInvoke("1", [][]byte{[]byte("get"), []byte("a")})
fmt.Println("The new value of a is", string(res.Payload))
}
在当前目录执行go test,输出结果如下

还可以查看更详细的测试结果,如覆盖率:
go test -cover -covermode count -coverprofile ./cover.out
输出结果,可以看见覆盖率为68.8%,覆盖率越高说明测试用例写的越完整。

进一步执行以下命令可以将刚刚生成的cover.out文件转化为html页面在浏览器中更具体的看见测试的覆盖程度。
go tool cover -html=./cover.out

实际测试的时候对每个接口都应该有不止一个case,需要考虑到反例或其他边界条件,还可以在测试时将预期得到的结果与实际得到的结果进行比较,如果不一致就报错使用例不显示PASS。
性能测试
性能测试的函数必须以Benchmark开头,接收的参数类型为*testing.B。这里我将一次存储和查询合并为一次操作(operation)来进行测试,代码如下:
func BenchmarkFunc(b *testing.B) {
cc := new(SimpleAsset)
stub := shim.NewMockStub("sacc", cc)
for i :=0 ; i< b.N; i++ {
stub.MockInvoke("1", [][]byte{[]byte("set"), []byte("a"), []byte("100")})
stub.MockInvoke("1", [][]byte{[]byte("get"), []byte("a")})
}
}
循环的次数为 b.N,并且每次测试时整个函数会被执行三次,N的数量会不断增加,如100, 10k, 300k。
执行测试:
go test --benchmem -bench=.
测试结果如图,ns/op 指的是平均每次操作花费的纳秒数,B/op指平均每次操作占用的内存大小。

由于实际情况下chaincode的接口是面向状态数据库的,而这里是用内存的读写来模拟的,所以这里的性能测试显得意义不是很大,但是如果链码中存在一些比较耗时的计算等操作,还是可以性能测试一下的。
总结
使用开发者 (dev) 模式进行测试:
- 好处是网络规模简单,可以在终端中直接看到链码打印的日志,使用cli命令行容器测试也比较方便(可以写成测试脚本映射到cli容器中自动执行)。
- 不足之处为每次修改链码后还是需要重新启动整个网络,再次编译、安装和实例化链码,不过这些操作都可以写成一个脚本一键完成。
使用单元测试:
- 好处是不需要启动网络环境,一条简单的命令就可以在本地自动化执行,且可以帮助我们很规范地对接口进行完整的测试。
- 不足之处是目前还无法测试基于couchDB的富查询操作。
原文链接:https://zhayujie.com/chaincode-test.html
Fabric Chaincode调试 —— 开发者模式和单元测试的更多相关文章
- 第二篇 :微信公众平台开发实战Java版之开启开发者模式,接入微信公众平台开发
第一部分:微信公众号对接的基本介绍 一.填写服务器配置信息的介绍 登录微信公众平台官网后,进入到公众平台后台管理页面. 选择 公众号基本设置->基本配置 ,点击“修改配置”按钮,填写服务器地址( ...
- Android真机测试、乐视手机启用开发者模式
一.乐视手机启用开发者模式 1.进入 设置>关于手机,连续按5次,进入开发者模式 显示结果如下: 2.启用开发者模式,并且要启用USB调试 3.在VS中部署或调试Android引用,使用真机测试 ...
- PowerShell将Windows store应用程序安装为开发者模式
原文: PowerShell将Windows store应用程序安装为开发者模式 在本地部署Windows 商店应用程序时,我们会遇到Add-AppDevPackage.ps1脚本,这个脚本和所在安装 ...
- 后端开发之chrome开发者模式
1. 场景描述 java开发前后端分离模式越来越流行,后端人员可以直接使用swagger进行接口调试(前后端分离之Swagger2),但是调试的时候,需要设置入参,假如该模块不是软件老王开发的,接别人 ...
- Android Studio [真机测试/开发者模式]
一.手机设置 首先根据自己的手机型号百度打开开发者模式, 我的是vivo,设置--->更多设置-->关于手机-->软件版本号连续点击会提示开启开发者模式. 并在开发者选项里打开USB ...
- PHP实现开发者模式出现该公众号提供的服务出现故障 请稍后再试解决方法
PHP实现开发者模式出现该公众号提供的服务出现故障 请稍后再试解决方法 仔细检查下有没有echo等输出的代码 echo没有输出东西 就是报这个信息 所以调试信息都必须写入日记
- 第一章 使用开发者模式快速入门 Odoo 12
本文为最好用的免费ERP系统Odoo 12开发手册系列文章第一篇. Odoo提供了一个快速应用开发框架,非常适合创建商业应用.这类应用通常用于保留业务记录,增删改查操作.Odoo 不仅简化了这类应用的 ...
- 《C#微信开发系列(1)-启用开发者模式》
1.0启用开发者模式 ①填写服务器配置 启用开发模式需要先成为开发者,而且编辑模式和开发模式只能选择一个(进入微信公众平台=>开发=>基本配置)就可以看到以下的界面: 点击修改配置,会出现 ...
- MIUI5(红米、小米)打开开发者模式
在miui5系统中系统默认隐藏原生android的开发者模式选项,要想启动该模式需要按照以下操作: 设置-关于手机- 连续点击安卓版本4下. 然后再返回主设置页面下,你会发现开发者选项已经出现.
- python Chrome 开发者模式消失的方法
最近使用 Chrome浏览器跑Selenium Python 自动化脚本运行过程中,总是出现这样的对话框 出现这样的对话框,如果不能自动关闭,这个对话框会影响web端页面的其他链接的定位识别,这样就 ...
随机推荐
- 「Log」2023.9.26 小记
序幕 \(\text{6:40}\):到校,整大量博客. 今天有模拟赛,发题的时候就我一个人,差点以为自己要 rk1 了. \(\text{7:30}\):开题. 发现 T1 做过,简单缩点+树直径速 ...
- 总决赛定档!“天翼云息壤杯”高校AI大赛巅峰之战即将打响!
近日,为梦想添翼,让AI发光--"天翼云息壤杯"高校AI大赛总决赛时间正式揭晓.总决赛将于2025年7月1日至7月17日在北京举办.届时,来自全国各地上百支成功晋级的优秀队伍和特邀 ...
- pip安装模块提示Command "python setup.py egg_info" failed with error code 1
报错详情: [root@k8s001 ~]# pip install kubernetes Collecting kubernetes Using cached https://files.pytho ...
- gRPC 学习了解记录
背景 项目中需要用到gRPC,所以需要去了解它的使用.去官网看它的介绍以及run Quick start run quickStart 遇到问题 根据官网的介绍,run Quick start 的时候 ...
- ArcObjects SDK 019 SpatialReference
1.SpatialReference的结构 ArcObjects SDK帮助中Esri.ArcGIS.Geometry命名空间帮助中的Object Model Diagram共两页,第一页就是Geom ...
- hot100之数组
最大子数组和(053) 先看代码 class Solution { public int maxSubArray(int[] nums) { int n = nums.length; int subS ...
- Java源码分析系列笔记-6.ReentrantLock
目录 1. 是什么 1.1. synchronized vs ReentranLock 2. 实现原理 2.1. uml图 3. 公平锁 3.1. 如何使用 3.2. 原理分析 3.2.1. 构造方法 ...
- 本地搭建一个对嘴AI工具
图片+音频=说话视频 这就是本次需要实现的功能. 一:环境 window10电脑(GPU越大越好,我的是专享8G,有点小了). Python 3.11.9. CUDA Version: 12.9.(驱 ...
- 前端开发系列026-基础篇之Canvas绘图(曲线)
本文将介绍Canvas中的弧度.曲线.圆弧以及文字的绘制方法以及径向渐变等内容,并提供饼状图等综合案例. 一.Canvas中的弧度.曲线和圆弧 专业术语 夹角 从一个点发射(延伸)出两条线段,两条线相 ...
- NPM 自动管理包依赖
简介 包依赖的实例 可以时刻保持 模块是最新的js版本 code { "name":"d3-project-template", "version&q ...