## 前言
在fabric开发中,chaincode的测试是一个比较麻烦的事情,原因如下:
1. 由于实际情况中的链码的存储和查询依赖于peer节点上的状态数据库,导致无法在本地测试。
2. 由于链码是运行于容器中的,者就导致我们很难获取代码执行过程中的日志,如果直接在实际实际开发环境中测试链码,是很麻烦的,每一次都必须重启整个网络,并且要重新创建和加入通道,安装和实例化链码,验证影响了开发效率。
## 两种测试方案
### 开发者模式
#### 环境分析
使用开发者调试环境,需要先下载,fabric-samples:
```
cd $GOPATH/src/github.com/hyperledger
git clone https://github.com/hyperledger/fabric-samples.git
```
开发者模式目录位于:
`fabric-samples/chaincode-docker-devmode`
进入目录:
`cd fabric-samples/chaincode-docker-devmode`
我们来分析一下`docker-compose-simple.yaml`文件。
> 这个文件中包括一个orderer节点、一个peer节点、一个cli容器、一个chaincode容器,其中cli容器负责发送请求来测试链码,chaincode容器负责运行链码
##### cli容器分析:
1. 容器名称为cli
2. 容器镜像使用`hyperledger/fabric-tools`
3. 容器启动之后,会执行`/bin/bash -c './script.sh'`运行`sh`脚本,该脚本会自动创建名为`myc`的通道,并将节点加入
4. 通过以上分析,我们了解到,我们编写的链码只需要安装和实例化即可
##### chaincode分析
我们看到`volumes`中有这样一条映射配置
`- ./../chaincode:/opt/gopath/src/chaincode`这表示将宿主机的`./../chaincode`目录,映射到容器的`/opt/gopath/src/chaincode`目录,这也是我们测试的链码需要放置的地方,为了方便管理,我们可以在该目录下再为每个链码创建一个目录,然后将我们的链码代码放置到该目录,当然也可以随便创建目录,然后在配置文件中添加一下映射。
### 测试过程
我们直接通过`fabric-samples/chaincode`目录下面的`sacc`链码进行测试,这个链码很简单,大家可以看下源码
#### 启动开发模式
`docker-compose -f docker-compose-simple.yaml up -d`
#### 安装和实例化链码
`docker exec -it chaincode bash`进入链码容器
安装和实例化:
```
peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v 0
peer chaincode instantiate -n mycc -v 0 -c``'{"Args":["a","10"]}'``-C myc
```
#### 在cli容器中测试链码
`docker exec -it cli bash`,进入容器
进行测试:
调用`set()`接口,设置a的值为20
`peer chaincode invoke -n mycc -c``'{"Args":["set", "a", "20"]}'``-C myc`
调用`get()`接口,查询a的值,结果为20,
`peer chaincode query -n mycc -c``'{"Args":["get","a"]}'``-C myc`
至此,测试完毕
**如果在链码的开发中使用了`couchdb`提供富文本查询,则需要在测试环境中加入`couchdb`容器**,具体如下:
对`docker-compose-simple.yaml`,添加如下配置:
```
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-value`的状态数据库,链码调用`PubStat()`和`GetStat()`其实就是作用于内存中的`map`。
`MockStub`主要提供两个函数来模拟背书节点对链码的调用:`MockInit()`和`MockInvoke()`,分别调用`Init`和`Invoke`接口。接受的参数类型为`string`类型的`uuid`(随便设置即可),以及一个二维`byte`数组(用于测试的提供参数)。
### 单元测试的要求
1. 需要导入`testing`包
2. 单元测试文件以`_test.go`结尾
3. 单元测试用例的函数必须以`Test`开头
### 以下是一个单元测试的例子
>下面是对`sacc.go`的单元测试例子,由于改代码比较简单,这里就讲几个接口的测试写在一个`case`里,创建`sacc_test.go`文件,代码如下:
```
package te
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`
进一步执行以下命令可以将刚刚生成的 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,200,300。
执行测试:
`go test --benchmem -bench=.`
**测试结果说明:**
* ns/op 指的是平均每次操作花费的纳秒数
* B/op指平均每次操作占用的内存大小
由于实际情况下chaincode的接口是面向状态数据库的,而这里是用内存的读写来模拟的,所以这里的性能测试显得意义不是很大,但是如果链码中存在一些比较耗时的计算等操作,还是可以性能测试一下的。
## 总结
**开发者模式:**
好处是网络规模简单,可以在终端中直接看到链码打印的日志,使用cli命令行容器测试也比较方便(可以写成测试脚本映射到cli容器中自动执行)。
不足之处为每次修改链码后还是需要重新启动整个网络,再次编译、安装和实例化链码,不过这些操作都可以写成一个脚本一键完成。
**单元测试:**
好处是不需要启动网络环境,一条简单的命令就可以在本地自动化执行,且可以帮助我们很规范地对接口进行完整的测试。
不足之处是目前还无法测试基于couchDB的富查询操作。
参考:[https://www.cnblogs.com/skzxc/p/12150476.html](https://www.cnblogs.com/skzxc/p/12150476.html)
- 空白目录
- CentOS 7安装配置Shadowsocks客户端
- CentOS 7.3 安装 libsodium 1.0.18
- npm安装(Centos7)——CentOS7常用软件
- Fabric
- Hyperledger Fabric1.4.2 主要更新内容:从Kafka迁移到Raft
- Fabric1.0 交易流程
- v1.4
- Hyperledger Fabric v1.4(LTS) 系列(1) - Fabric 的第一个LTS版本
- Hyperledger Fabric v1.4(LTS) 系列(2) - 简介
- Hyperledger Fabric v1.4(LTS) 系列(3.1) -关键概念
- Hyperledger Fabric v1.4(LTS) 系列(3.2):关键概念-功能性
- Hyperledger Fabric v1.4(LTS) 系列(3.3):关键概念-模型
- Hyperledger Fabric v1.4(LTS) 系列(3.4):关键概念-区块链网络
- HyperLedger Fabric 1.4 智能合约 Helloworld运行(9)
- 常见错误
- hyperledger-fabric的Error: got unexpected status: BAD_REQUEST错误(执行Building Your First Network时)
- 操作指南
- first_network
- 命令
- peer
- peer
- 链码测试
- 交易流程
- fabric共识机制
- etcdraft算法
- 人才发展
- 密码学
- 区块链背后的密码学原理
- 椭圆曲线加密算法
- 共识机制
- pow共识机制
- pos共识机制
- dpos共识机制
- POI共识机制
- pop共识机制
- 共识机制总结
- Bitcoin
- 白皮书
- 网络
- p2p