Test Method Fabric chain code (go Language Unit Test / Performance Test)

Fabric chaincode test - developer mode and unit testing

[Link] Reference: https://blog.csdn.net/zhayujie5200/article/details/84561825

Foreword

In fabric development, chaincode test is a very troublesome issue, first, because of the actual situation in chaincode store and query are dependent on the state of the database on a peer node, it can not be directly tested locally; the second is due chaincode is running in the container, which leads to difficult for us to get printed journal in code. If the actual development environment directly in the test chaincode very troublesome, and each time the debugger will need to restart the entire network (there may or multi-machine deployments), and to create and join channel, installation and instantiated chain code, which seriously affected the efficiency of the test.

Here chain code means for the two tests, one is the developer (dev) mode, a simple network built to test single locally; the other is a unit testing (UT), the node may not need to start the environment, automated testing all interfaces .

Developer mode

Environmental analysis
using a developer debugging environment, you need to download the fabric-samples, placed under $ GOPATH / src. Developers debug directory located: Fabric-the Samples / chaincode-Docker-DEVMODE

  • First, analyze the docker-compose-simple.yaml files in the directory: The network contains an orderer node, a peer node, a chaincode container (responsible for running the chain code we want to test), a cli container (responsible for sending a request to test the chain code).

There are two points to note:

Cli command entry can be seen in the container, script.sh will automatically execute the script in the current directory after startup, the script will automatically create a channel called myc, and the node is added. So we only need to install and can be instantiated chain code.
In the volumes chaincode container can be seen in such a mapping:

- ./../chaincode:/opt/gopath/src/chaincode

Description fabric-samples / chaincode directory is mapped into the container, and this is where we need to be tested chain code is placed. In order to facilitate the management, we can redistribution of a directory for each chain code in the directory, and then be tested chain code on them. (Of course, you can also directly modify the mapping to the actual path of their own chaincode) .

Testing process
here in the simplest sacc.go for example, the chain code covers only a simple memory (set) and query (get) function. The whole process needs to be started three terminals:

Terminal One: Start the network
first enter developer mode catalog:

cd fabric-samples/chaincode-docker-devmode

Start the network :

docker-compose -f docker-compose-simple.yaml up

It represents a successful start when you see Going to wait for newer blocks, this time there are four container (1 orderer, 1 peer, 1 chaincode, 1 cli) network, create a channel myc and peer successfully joined.

The terminal 2: chain code compiled
into the container chaincode

docker exec -it chaincode bash

Compile want chaincode test:

cd sacc
go build

After the successful implementation of a single executable file will be generated under the current directory. At this point you need to start the executable file:

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数组(用于测试的提供参数)。

单元测试的要求:

1.需要导入testing

2.单元测试文件以_test.go结尾

3.测试用例的函数必须以Test开头

单元测试的例子
下面是对sacc.go的单元测试例子,由于该代码较简单,这里就将几个接口的测试写在一个case里。创建sacc_test.go文件,测试用例如下:

package packagename

import (
  "fmt"
  "github.com/hyperledger/fabric/core/chaincode/shim"
  "testing"
)

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)来进行测试,代码如下:(将下面的代码添加到上一步中的 sacc_test.go文件中!)

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的富查询操作。

Guess you like

Origin www.cnblogs.com/skzxc/p/12150476.html