首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

对使用 Go 为 Hyperledger Fabric v0.6 编写的区块链链代码进行单元测试(3)实现

对使用 Go 为 Hyperledger Fabric v0.6 编写的区块链链代码进行单元测试(3)实现

我们采用测试驱动开发来实现 sample_chaincode.go 文件中的 CreateLoanApplication 方法。
要求
  • CreateLoanApplication 方法应获取以下输入:一个贷款申请 ID、一个表示要创建的贷款申请的                    JSON 字符串,以及 ChaincodeStubInterface,后者将用于与底层 Hyperledger                    Fabric 基础架构进行通信。
  • 它应返回两个参数:表示所创建的贷款申请的序列化 JSON 字符串,以及一个 error 对象。
  • 如果缺少输入/输入无效,则会抛出验证错误。
清单 3. 第一个测试的代码
1
2
3
4
5
6
7
8
9
func TestCreateLoanApplication (t *testing.T) {
    fmt.Println("Entering TestCreateLoanApplication")
    attributes := make(map[string][]byte)
    //Create a custom MockStub that internally uses shim.MockStub
    stub := shim.NewCustomMockStub("mockStub", new(SampleChaincode), attributes)
    if stub == nil {
        t.Fatalf("MockStub creation failed")
    }
}




如清单 3 所示,所有测试函数都以 “Test” 关键字开头,以便 Golang 测试包可以识别并运行这些函数。测试函数接受                testing.T 参数,该参数将提供对可用于编写测试的帮助器方法的访问。
依据清单 2 中所示的要求,CreateLoanApplication 方法应接受                ChaincodeStubInterface 作为其参数。因为 Hyperledger Fabric 在运行时会将                ChaincodeStubInterface 的实际实例传入 Query/Invoke/Init                方法中,所以您需要模拟 ChaincodeStubInterface 来实现单元测试。
在清单 3 中,第 5 行创建了一个新的                CustomMockStub,该函数接受名称、(您打算实现的)SampleChaincode                对象和一个属性图作为参数。这里创建的桩代码是 前面讨论过的 一段自定义模拟桩代码。
现在从包含 sample_chaincode_test.go 文件的 root 文件夹运行 go test                来执行此测试。您的输出应类似于:
1 bash-3.2$ go test
2 can't load package: package .:
3 sample_chaincode.go:1:1:1 expected 'package', found 'EOF'




和预期一样,测试失败了,因为 sample_chaincode.go                    文件是空的,甚至连包语句都没有。这表示测试处于红色阶段。
现在我们来编写通过此测试所需的最少量代码。将下面这行添加到 sample_chaincode.go 文件:
清单 4. 为了通过测试而需要向 sample_chaincode.go                添加的最少代码
1
package main




再次运行测试。测试失败并抛出以下错误:
1 ./sample_chaincode_test.go:18: undefined: SampleChaincode




测试失败是因为,sample_chaincode.go 文件没有定义 SampleChaincode。
让我们将此代码添加到 sample_chaincode.go 文件中:
清单 5. 向 sample_chaincode.go                添加另一段代码
1
2
type SampleChaincode struct {
}




再次运行测试。它仍将失败并抛出以下错误:
1 ./sample_chaincode_test.go:16: cannot use new (SampleChaincode)
2 (type *SampleChaincode) as type shim.Chaincode in argument to
3 shim.NewMockStub:
4        *SampleChaincode does not implement shim.Chaincode
5 (missing Init method)




测试失败是因为 CustomMockStub 要求 SampleChaincode 实现                    Init、Query 和 Invoke 方法,然后才会将其视为                shim.Chaincode 类型的实例。
现在将以下代码添加到 sample_chaincode.go:
清单 6. 向 sample_chaincode.go                添加另一段代码
1
2
3
4
5
6
7
8
9
10
11
func (t *SampleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    return nil, nil
}

func (t *SampleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    return nil, nil
}

func (t *SampleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    return nil, nil
}




再次运行测试时,测试通过了。这是测试的绿色阶段。
1 bash-3.2$ go test
2 Entering TestCreateLoanApplication
3 2017/02/22 19:10:08 MockStub( mockStub &{} )
4 PASS




将 CreateLoanApplication 方法添加到                sample_chaincode.go:
清单 7. 将 CreateLoanApplication 方法添加到                    sample_chaincode.go
1
2
3
4
func CreateLoanApplication(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
    fmt.Println("Entering CreateLoanApplication")
    return nil, nil
}




添加以下测试,以确保从 CreateLoanApplication 方法返回了一个验证错误来响应空输入参数。
清单 8. 添加针对验证错误的测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func TestCreateLoanApplicationValidation(t *testing.T) {
    fmt.Println("Entering TestCreateLoanApplicationValidation")
    attributes := make(map[string][]byte)
    stub := shim.NewCustomMockStub("mockStub", new(SampleChaincode), attributes)
    if stub == nil {
        t.Fatalf("MockStub creation failed")
    }

    stub.MockTransactionStart("t123")
    _, err := CreateLoanApplication(stub, []string{})
    if err == nil {
        t.Fatalf("Expected CreateLoanApplication to return validation error")
    }
    stub.MockTransactionEnd("t123")
}




请注意 stub.MockTransactionStart(“t123”) 和 stub.MockTransactionStop(“t123”)                调用。因为写入账本的任何信息都需要位于交易上下文中,所以测试必须在调用 CreateLoanApplication 方法之前启动交易,因为                CreateLoanApplication 方法会将贷款申请保存到账本中。然后必须结束具有相同 ID 的交易,以表明交易完成。
使用 go test 运行测试。
1 bash-3.2$ go test
2 Entering TestCreateLoanApplication
3 2017/02/22 22:55:52 MockStub( mockStub &{} )
4 Entering CreateLoanApplication
5 --- FAIL: TestCreateLoanApplicationValidation (0.00s)
6         sample_chaincode_test.go:35: Expected CreateLoanApplication to
          return validation error
7 FAIL
8 exit status 1




跟预期一样,测试失败了。现在向 sample_chaincode.js 添加通过测试所需的最少量代码:
清单 9. 为了通过测试而需要向 sample_chaincode.js                添加的最少量代码
1
2
3
4
func CreateLoanApplication(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
    fmt.Println("Entering CreateLoanApplication")
    return nil, errors.New(“Expected atleast two arguments for loan application creation”)
}




再次使用 go test 运行测试。
1 bash-3.2$ go test
2 Entering TestCreateLoanApplication
3 2017/02/22 23:02:52 MockStub( mockStub &{} )
4 Entering CreateLoanApplication
5 PASS




测试通过。这是测试的绿色阶段,因为 CreateLoanApplication                方法会始终返回一个错误。现在编写另一个测试,该测试将揭示此缺陷并导致代码重构。
清单 10. 一个揭示缺陷的新测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var loanApplicationID = "la1"
var loanApplication = `{"id":"` + loanApplicationID + `","propertyId":"prop1","landId":"land1","permitId":"permit1","buyerId":"vojha24","personalInfo":{"firstname":"Varun","lastname":"Ojha","dob":"dob","email":"varun@gmail.com","mobile":"99999999"},"financialInfo":{"monthlySalary":16000,"otherExpenditure":0,"monthlyRent":4150,"monthlyLoanPayment":4000},"status":"Submitted","requestedAmount":40000,"fairMarketValue":58000,"approvedAmount":40000,"reviewedBy":"bond","lastModifiedDate":"21/09/2016 2:30pm"}`
  
  func TestCreateLoanApplicationValidation2(t *testing.T) {
    fmt.Println("Entering TestCreateLoanApplicationValidation2")
    attributes := make(map[string][]byte)
    stub := shim.NewCustomMockStub("mockStub", new(SampleChaincode), attributes)
    if stub == nil {
        t.Fatalf("MockStub creation failed")
    }

    stub.MockTransactionStart("t123")
    _, err := CreateLoanApplication(stub, []string{loanApplicationID, loanApplication})
    if err != nil {
        t.Fatalf("Expected CreateLoanApplication to succeed")
    }
    stub.MockTransactionEnd("t123")

}




第 1 和第 2 行将为贷款申请创建测试数据,这些数据被用作 CreateLoanApplication 方法的参数。
现在运行该测试。跟预期一样,测试将失败。
1 Entering TestCreateLoanApplicationValidation2
2 2017/02/22 23:09:01 MockStub( mockStub &{} )
3 Entering CreateLoanApplication
4 --- FAIL: TestCreateLoanApplicationValidation2 (0.00s)
5         sample_chaincode_test.go:55 Expected CreateLoanApplication to succeed
6 FAIL
7 exit status 1




现在,重构 sample_chaincode.js 中的 CreateLoanApplication 代码,以便通过此测试。
返回列表