在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(),分别调用InitInvoke接口。接收的参数均为类型为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调试 —— 开发者模式和单元测试的更多相关文章

  1. 第二篇 :微信公众平台开发实战Java版之开启开发者模式,接入微信公众平台开发

    第一部分:微信公众号对接的基本介绍 一.填写服务器配置信息的介绍 登录微信公众平台官网后,进入到公众平台后台管理页面. 选择 公众号基本设置->基本配置 ,点击“修改配置”按钮,填写服务器地址( ...

  2. Android真机测试、乐视手机启用开发者模式

    一.乐视手机启用开发者模式 1.进入 设置>关于手机,连续按5次,进入开发者模式 显示结果如下: 2.启用开发者模式,并且要启用USB调试 3.在VS中部署或调试Android引用,使用真机测试 ...

  3. PowerShell将Windows store应用程序安装为开发者模式

    原文: PowerShell将Windows store应用程序安装为开发者模式 在本地部署Windows 商店应用程序时,我们会遇到Add-AppDevPackage.ps1脚本,这个脚本和所在安装 ...

  4. 后端开发之chrome开发者模式

    1. 场景描述 java开发前后端分离模式越来越流行,后端人员可以直接使用swagger进行接口调试(前后端分离之Swagger2),但是调试的时候,需要设置入参,假如该模块不是软件老王开发的,接别人 ...

  5. Android Studio [真机测试/开发者模式]

    一.手机设置 首先根据自己的手机型号百度打开开发者模式, 我的是vivo,设置--->更多设置-->关于手机-->软件版本号连续点击会提示开启开发者模式. 并在开发者选项里打开USB ...

  6. PHP实现开发者模式出现该公众号提供的服务出现故障 请稍后再试解决方法

    PHP实现开发者模式出现该公众号提供的服务出现故障 请稍后再试解决方法 仔细检查下有没有echo等输出的代码  echo没有输出东西 就是报这个信息  所以调试信息都必须写入日记

  7. 第一章 使用开发者模式快速入门 Odoo 12

    本文为最好用的免费ERP系统Odoo 12开发手册系列文章第一篇. Odoo提供了一个快速应用开发框架,非常适合创建商业应用.这类应用通常用于保留业务记录,增删改查操作.Odoo 不仅简化了这类应用的 ...

  8. 《C#微信开发系列(1)-启用开发者模式》

    1.0启用开发者模式 ①填写服务器配置 启用开发模式需要先成为开发者,而且编辑模式和开发模式只能选择一个(进入微信公众平台=>开发=>基本配置)就可以看到以下的界面: 点击修改配置,会出现 ...

  9. MIUI5(红米、小米)打开开发者模式

    在miui5系统中系统默认隐藏原生android的开发者模式选项,要想启动该模式需要按照以下操作: 设置-关于手机- 连续点击安卓版本4下. 然后再返回主设置页面下,你会发现开发者选项已经出现.

  10. python Chrome 开发者模式消失的方法

    最近使用 Chrome浏览器跑Selenium Python 自动化脚本运行过程中,总是出现这样的对话框  出现这样的对话框,如果不能自动关闭,这个对话框会影响web端页面的其他链接的定位识别,这样就 ...

随机推荐

  1. 「Log」2023.9.26 小记

    序幕 \(\text{6:40}\):到校,整大量博客. 今天有模拟赛,发题的时候就我一个人,差点以为自己要 rk1 了. \(\text{7:30}\):开题. 发现 T1 做过,简单缩点+树直径速 ...

  2. 总决赛定档!“天翼云息壤杯”高校AI大赛巅峰之战即将打响!

    近日,为梦想添翼,让AI发光--"天翼云息壤杯"高校AI大赛总决赛时间正式揭晓.总决赛将于2025年7月1日至7月17日在北京举办.届时,来自全国各地上百支成功晋级的优秀队伍和特邀 ...

  3. pip安装模块提示Command "python setup.py egg_info" failed with error code 1

    报错详情: [root@k8s001 ~]# pip install kubernetes Collecting kubernetes Using cached https://files.pytho ...

  4. gRPC 学习了解记录

    背景 项目中需要用到gRPC,所以需要去了解它的使用.去官网看它的介绍以及run Quick start run quickStart 遇到问题 根据官网的介绍,run Quick start 的时候 ...

  5. ArcObjects SDK 019 SpatialReference

    1.SpatialReference的结构 ArcObjects SDK帮助中Esri.ArcGIS.Geometry命名空间帮助中的Object Model Diagram共两页,第一页就是Geom ...

  6. hot100之数组

    最大子数组和(053) 先看代码 class Solution { public int maxSubArray(int[] nums) { int n = nums.length; int subS ...

  7. Java源码分析系列笔记-6.ReentrantLock

    目录 1. 是什么 1.1. synchronized vs ReentranLock 2. 实现原理 2.1. uml图 3. 公平锁 3.1. 如何使用 3.2. 原理分析 3.2.1. 构造方法 ...

  8. 本地搭建一个对嘴AI工具

    图片+音频=说话视频 这就是本次需要实现的功能. 一:环境 window10电脑(GPU越大越好,我的是专享8G,有点小了). Python 3.11.9. CUDA Version: 12.9.(驱 ...

  9. 前端开发系列026-基础篇之Canvas绘图(曲线)

    本文将介绍Canvas中的弧度.曲线.圆弧以及文字的绘制方法以及径向渐变等内容,并提供饼状图等综合案例. 一.Canvas中的弧度.曲线和圆弧 专业术语 夹角 从一个点发射(延伸)出两条线段,两条线相 ...

  10. NPM 自动管理包依赖

    简介 包依赖的实例 可以时刻保持 模块是最新的js版本 code { "name":"d3-project-template", "version&q ...