相关文章推荐
英勇无比的签字笔  ·  动态链接库的全局变量的导出和类的导出_vs2 ...·  10 月前    · 
开朗的烈酒  ·  见证服务器如何影响数据库可用性 - SQL ...·  1 年前    · 
朝气蓬勃的电脑桌  ·  Use .NET to connect ...·  1 年前    · 
傻傻的瀑布  ·  flex 布局导致子项高度相同,如何解决 - 掘金·  1 年前    · 
千杯不醉的红豆  ·  “Gm”选项已否决,并将在将来的版本中移除, ...·  1 年前    · 
小百科  ›  Chaincode调试 —— 开发者模式和单元测试-腾讯云开发者社区-腾讯云
容器技术 接口测试 单元测试 couchdb
神勇威武的蟠桃
1 年前
作者头像
zhayujie
0 篇文章

Chaincode调试 —— 开发者模式和单元测试

前往专栏
腾讯云
开发者社区
文档 意见反馈 控制台
首页
学习
活动
专区
工具
TVP
文章/答案/技术大牛
发布
首页
学习
活动
专区
工具
TVP
返回腾讯云官网
社区首页 > 专栏 > ZYJ的技术专栏 > Chaincode调试 —— 开发者模式和单元测试

Chaincode调试 —— 开发者模式和单元测试

作者头像
zhayujie
修改 于 2020-05-12 23:58:08
1.2K 0
修改 于 2020-05-12 23:58:08
举报

在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)
 
推荐文章
英勇无比的签字笔  ·  动态链接库的全局变量的导出和类的导出_vs2022 生成动态库时全局变量的导出-CSDN博客
10 月前
开朗的烈酒  ·  见证服务器如何影响数据库可用性 - SQL Server Database Mirroring | Microsoft Learn
1 年前
朝气蓬勃的电脑桌  ·  Use .NET to connect and query a database on Windows, Linux, or macOS - Azure SQL Database & SQL Managed Instance | Microsoft Learn
1 年前
傻傻的瀑布  ·  flex 布局导致子项高度相同,如何解决 - 掘金
1 年前
千杯不醉的红豆  ·  “Gm”选项已否决,并将在将来的版本中移除,“/ZI”和“/Gy-”命令行选项不兼容_gm选项已否决_weixin_51562864的博客-CSDN博客
1 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
小百科 - 百科知识指南
© 2024 ~ 沪ICP备11025650号