对使用 Go 为 Hyperledger Fabric v0.6 编写的区块链链代码进行单元测试(5)
- UID
- 1066743
|
对使用 Go 为 Hyperledger Fabric v0.6 编写的区块链链代码进行单元测试(5)
Invoke 方法实现让我们使用测试驱动开发来实现 shim.Chaincode.Invoke 方法。Invoke 方法由链代码基础架构调用,它传入 ChaincodeStubInterface 的合适实例,以及链代码的调用方(客户应用程序)所传入的函数名和参数。
要求- Invoke 方法应检查输入函数名称参数,并将执行任务委托给合适的处理函数。
- 如果输入函数名称是无效的,Invoke 方法会返回一个错误。
- Invoke 方法应基于链代码的调用方的交易证书,实现/委托访问控制和权限管理。应该只允许 Bank_Admin 调用 CreateLoanApplication 方法。
第一个测试将验证上面的 “要求 3” 中列出的功能。
清单 14. TestInvokeValidation 测试的代码段1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| func TestInvokeValidation(t *testing.T) {
fmt.Println("Entering TestInvokeValidation")
attributes := make(map[string][]byte)
attributes["username"] = []byte("vojha24")
attributes["role"] = []byte("client")
stub := shim.NewCustomMockStub("mockStub", new(SampleChaincode), attributes)
if stub == nil {
t.Fatalf("MockStub creation failed")
}
_, err := stub.MockInvoke("t123", "CreateLoanApplication", []string{loanApplicationID, loanApplication})
if err == nil {
t.Fatalf("Expected unauthorized user error to be returned")
}
}
|
第 1 部分 中已经解释过,链代码的调用方的交易证书可能包含用户定义的属性。这些属性为在链代码中执行访问控制和权限发挥着关键作用。
第 5 和第 6 行添加用户名和角色属性,然后这些属性被传递给 CustomMockStub 构造函数。这些属性应有助于模拟可从链代码调用方的交易证书检索的属性。
第 13 行使用 stub.MockInvoke 方法模拟链代码基础架构在运行时应如何直接调用 shim.Chaincode.Invoke 方法。
MockInvoke 方法接受交易 ID(由区块链基础架构在运行时生成)、函数名和输入参数。
再次运行该测试套件。跟预期一样,TestInvokeValidation 测试将会失败。这是测试的红色阶段。
1 --- FAIL: TestInvokeValidation (0.00s)
2 sample_chaincode_test.go:158 Expected unauthorized user error to be returned
3 FAIL
4 exit status 1
|
现在,在 sample_chaincode.go 中的 Invoke 方法中编写通过此测试所需的最少量代码。这是测试的绿色阶段。
清单 15. 为了通过测试而需要在 sample_chaincode.go 中的 Invoke 方法中包含的最少量代码1
2
3
4
| func (t *SampleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
fmt.Println("Entering Invoke")
return nil, errors.New("unauthorized user")
}
|
现在运行该测试套件。TestInvokeValidation 测试将会通过。
1 Entering TestInvokeValidation
2 2017/03/06 23:22:27 MockStub( mockStub &{} )
3 Entering Invoke
4 PASS
|
下一个测试将传入正确的角色 Bank_Admin 并期望测试通过。
清单 16. TestInvokeValidation2 的代码段1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| func TestInvokeValidation2(t *testing.T) {
fmt.Println("Entering TestInvokeValidation")
attributes := make(map[string][]byte)
attributes["username"] = []byte("vojha24")
attributes["role"] = []byte("Bank_Admin")
stub := shim.NewCustomMockStub("mockStub", new(SampleChaincode), attributes)
if stub == nil {
t.Fatalf("MockStub creation failed")
}
_, err := stub.MockInvoke("t123", "CreateLoanApplication", []string{loanApplicationID, loanApplication})
if err != nil {
t.Fatalf("Expected CreateLoanApplication to be invoked")
}
}
|
运行该测试套件。跟预期一样,TestInvokeValidation2 测试将会失败。要通过此测试,我们现在必须重构 sample_chaincode.go 中的 Invoke 的代码。
清单 17. 重构 sample_chaincode.go 中的 Invoke 方法代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| func (t *SampleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
fmt.Println("Entering Invoke")
ubytes, _ := stub.ReadCertAttribute("username")
rbytes, _ := stub.ReadCertAttribute("role")
username := string(ubytes)
role := string(rbytes)
if role != "Bank_Admin" {
return nil, errors.New("caller with " + username + " and role " + role + " does not have
access to invoke CreateLoanApplication")
}
return nil, nil
}
|
现在运行该测试套件。TestInvokeValidation2 测试将会通过。
清单 18. 测试要求 1 和要求 2 中列出的功能的代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| func TestInvokeFunctionValidation(t *testing.T) {
fmt.Println("Entering TestInvokeFunctionValidation")
attributes := make(map[string][]byte)
attributes["username"] = []byte("vojha24")
attributes["role"] = []byte("Bank_Admin")
stub := shim.NewCustomMockStub("mockStub", new(SampleChaincode), attributes)
if stub == nil {
t.Fatalf("MockStub creation failed")
}
_, err := stub.MockInvoke("t123", "InvalidFunctionName", []string{})
if err == nil {
t.Fatalf("Expected invalid function name error")
}
}
|
第 14 行验证是否从 Invoke 返回了合适的错误消息。
运行 TestInvokeFunctionValidation 测试。跟预期一样,它将失败并抛出以下输出:
1 --- FAIL: TestInvokeFunctionValidation (0.00s)
2 sample_chaincode_test.go:117 Expected invalid function name error
3 FAIL
4 exit status 1 |
|
|
|
|
|