对使用 Go 为 Hyperledger Fabric v0.6 编写的区块链链代码进行单元测试(3)实现
- UID
- 1066743
|
对使用 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 ./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.go1
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 代码,以便通过此测试。 |
|
|
|
|
|