github.com/s7techlab/cckit@v0.10.5/examples/insurance/README.md (about)

     1  # Tests for IBM Blockchain insurance app chaincodes with CCKit
     2  
     3  This example shows how to apply concepts of test-driven development to writing chaincode in Golang for Hyperledger Fabric 1.2
     4  using shim MockStub to unit-test your chaincode without having to deploy it in a Blockchain network.
     5  The use of described testing technique allowed us to find small bug in 
     6  [IBM blockchain insurance app](https://github.com/IBM/build-blockchain-insurance-app/pull/44)
     7  
     8   
     9  ##  IBM blockchain insurance app  
    10  
    11  Blockchain insurance application example https://developer.ibm.com/code/patterns/build-a-blockchain-insurance-app/  
    12  shows how through distributed ledger and smart contracts blockchain can transform insurance processes:
    13   
    14  >Blockchain presents a huge opportunity for the insurance industry. It offers the chance to innovate around the way
    15  data is exchanged, claims are processed, and fraud is prevented. Blockchain can bring together developers from tech 
    16  companies, regulators, and insurance companies to create a valuable new insurance management asset. 
    17   
    18  ![Architecture](app/images/arch-blockchain-insurance2.png)
    19  
    20  
    21  ### Source code 
    22  
    23  https://github.com/IBM/build-blockchain-insurance-app
    24  
    25  Chaincode from  https://github.com/IBM/build-blockchain-insurance-app/tree/master/web/chaincode/src/bcins is copied to  
    26  the [app](app) directory for sample test creation.
    27  
    28  ## Test-driven development
    29  
    30  Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle:
    31  requirements are turned into very specific test cases, then the software is improved to pass the new tests, 
    32  only. This is opposed to software development that allows software to be added that is not proven to meet requirements.
    33  
    34  When creating smart contracts on the Ethereum platform or chaincodes for Hyperledger Fabric, the developer programs a certain work logic 
    35  that determines how the methods should change the state of the smart contract / chaincodes , what events are to be emitted,
    36  when to return success or error response. This kind of software is ideal for development through testing.
    37  
    38  
    39  ## Why test with Mockstub
    40  
    41  Creating blockchain network and deploying the chaincode(s) to it is quite cumbersome and slow process, especially if your 
    42  code is constantly changing during developing process. 
    43  
    44  The Hyperledger Fabric [shim package](https://github.com/hyperledger/fabric/tree/release-1.2/core/chaincode/shim) 
    45  contains the [MockStub](https://github.com/hyperledger/fabric/blob/release-1.2/core/chaincode/shim/mockstub.go)
    46  implementation that wraps chaincode and stub out the calls to 
    47  [shim.ChaincodeStubInterface](https://github.com/hyperledger/fabric/blob/release-1.2/core/chaincode/shim/interfaces.go) 
    48  in chaincode functions. Mockstub allows you to get the test results almost immediately.
    49  
    50  
    51  MockStub contains implementation for most of `shim.ChaincodeStubInterface` function, but in the current version 
    52  of Hyperledger Fabric (1.4), the MockStub has not implemented some of the important methods such
    53  as `GetCreator`, for example. Since chaincode would use this method to get tx creator certificate
    54  for access control, it's critical to be able to stub this method  in order to completely unit-test chaincode. 
    55  
    56  `CCKit` contains extended [MockStub](../../testing/mockstub.go) with implementation of some of the unimplemented
    57  methods and delegating existing ones to shim.MockStub. [CCkit MockStub]((../../testing/mockstub.go)) holds a reference
    58  to the original MockStub and has additional methods and properties. 
    59  
    60  
    61  
    62  ## Tests for this application
    63  
    64  The tests are located in [current](.) directory:
    65  
    66  * [app_test.go](app_test.go) contains [Ginkgo](https://onsi.github.io/ginkgo/) based tests
    67  * [dto.go](dto.go) contains named DTO (data transfer objects) from original chaincode source code
    68  * [fixture.go](fixtures.go) contains fixtures for tests
    69  
    70  
    71  ## Getting started
    72  
    73  Before you begin, be sure to get `CCkit`:
    74  
    75  `git clone git@github.com:s7techlab/cckit.git`
    76  
    77  and get dependencies using `go mod` command:
    78  
    79  `go mod vendor`
    80  
    81  
    82  CCkit repository contains several examples:
    83  
    84  * [Cars](../cars)
    85  * [Marbles](../marbles)
    86  * And `insurance`
    87  
    88  All examples and this tutorial uses the [Ginkgo](https://onsi.github.io/ginkgo/) testing library, it hooks into Go’s existing testing infrastructure. This allows you to run a Ginkgo suite using go test.
    89  We import the ginkgo and gomega packages into the test’s top-level namespace by performing a dot-import. More about
    90  testing with Ginkgo you can read on [onsi.github.io/ginkgo/](https://onsi.github.io/ginkgo/)
    91  
    92  
    93  ## Creating test suite 
    94  
    95  Using separate [package with tests](.) instead of [chaincode package](app) allows us to respect
    96  the encapsulation of the chaincode package: your tests will need to import chaincode and access it from the outside.
    97  
    98  Test suite bootstrap example:
    99  
   100  ```go
   101  package main
   102  
   103  import (
   104  	"fmt"
   105  	"testing"
   106  	"github.com/s7techlab/cckit/examples/insurance/app"
   107  	. "github.com/onsi/ginkgo"
   108  	. "github.com/onsi/gomega"
   109  	testcc "github.com/s7techlab/cckit/testing"
   110  	expectcc "github.com/s7techlab/cckit/testing/expect"
   111  )
   112  
   113  func TestInsuranceApp(t *testing.T) {
   114  	RegisterFailHandler(Fail)
   115  	RunSpecs(t, "Insurance app suite")
   116  }
   117  
   118  var _ = Describe(`Insurance`, func() {
   119  
   120  	//Create chaincode mock
   121  	cc := testcc.NewMockStub(`insurance`, new(app.SmartContract))
   122  	
   123  	...
   124  	}
   125  }
   126  ```
   127  
   128   
   129  ## What to test 
   130  
   131  Chaincode interface functions described in file [main.go](main.go), so we can see all possible operations with chaincode data:
   132  
   133  * list contract types
   134  * create contract type
   135  * activate contract type
   136  * list contracts
   137  * list claims...
   138  
   139  ```go
   140  var bcFunctions = map[string]func(shim.ChaincodeStubInterface, []string) pb.Response{
   141  	// Insurance Peer
   142  	"contract_type_ls":         listContractTypes,
   143  	"contract_type_create":     createContractType,
   144  	"contract_type_set_active": setActiveContractType,
   145  	"contract_ls":              listContracts,
   146  	"claim_ls":                 listClaims,
   147  	"claim_file":               fileClaim,
   148  	"claim_process":            processClaim,
   149  	"user_authenticate":        authUser,
   150  	"user_get_info":            getUser,
   151  	// Shop Peer
   152  	"contract_create": createContract,
   153  	"user_create":     createUser,
   154  	// Repair Shop Peer
   155  	"repair_order_ls":       listRepairOrders,
   156  	"repair_order_complete": completeRepairOrder,
   157  	// Police Peer
   158  	"theft_claim_ls":      listTheftClaims,
   159  	"theft_claim_process": processTheftClaim,
   160  }
   161  ```
   162  Also chaincode has `init` function. So we can use them for testing insurance chaincode functionality.
   163  
   164  
   165  ## Data transfer objects and fixtures
   166  
   167  In this example we use [Data Transfer Objects](dto.go) (DTO) for creating chaincode args and
   168  [fixtures](fixtures.go)
   169  
   170  
   171  ## Test init function
   172  
   173  ```go
   174  Describe("Chaincode initialization ", func() {
   175  		It("Allow to provide contract types attributes  during chaincode creation [init]", func() {
   176  			expectcc.ResponseOk(cc.Init(`init`, &ContractTypesDTO{ContractType1}))
   177  		})
   178  	})
   179  ````
   180  
   181  ## Test operations with contract types
   182  
   183  ```go
   184  	Describe("Contract type ", func() {
   185  
   186  		It("Allow to retrieve all contract type, added during chaincode init [contract_type_ls]", func() {
   187  			contractTypes := expectcc.PayloadIs(
   188  				cc.Invoke(`contract_type_ls`), &ContractTypesDTO{}).(ContractTypesDTO)
   189  
   190  			Expect(len(contractTypes)).To(Equal(1))
   191  			Expect(contractTypes[0].ShopType).To(Equal(ContractType1.ShopType))
   192  		})
   193  
   194  		It("Allow to create new contract type [contract_type_create]", func() {
   195  			expectcc.ResponseOk(cc.Invoke(`contract_type_create`, &ContractType2))
   196  
   197  			// get contract type list
   198  			contractTypes := expectcc.PayloadIs(
   199  				cc.Invoke(`contract_type_ls`), &ContractTypesDTO{}).(ContractTypesDTO)
   200  
   201  			//expect now we have 2 contract type
   202  			Expect(len(contractTypes)).To(Equal(2))
   203  		})
   204  
   205  		It("Allow to set active contract type [contract_type_set_active]", func() {
   206  			Expect(ContractType2.Active).To(BeFalse())
   207  
   208  			// active ContractType2
   209  			expectcc.ResponseOk(cc.Invoke(`contract_type_set_active`, &ContractTypeActiveDTO{
   210  				UUID: ContractType2.UUID, Active: true}))
   211  		})
   212  
   213  		It("Allow to retrieve filtered by shop type contract types [contract_type_ls]", func() {
   214  			contractTypes := expectcc.PayloadIs(
   215  				cc.Invoke(`contract_type_ls`, &ShopTypeDTO{ContractType2.ShopType}),
   216  				&ContractTypesDTO{}).(ContractTypesDTO)
   217  
   218  			Expect(len(contractTypes)).To(Equal(1))
   219  			Expect(contractTypes[0].UUID).To(Equal(ContractType2.UUID))
   220  
   221  			// Contract type 2 activated on previous step
   222  			Expect(contractTypes[0].Active).To(BeTrue())
   223  		})
   224  	})
   225  ```
   226  
   227  ## Test operations with contracts
   228  
   229  ```go
   230  Describe("Contract", func() {
   231  
   232  		It("Allow everyone to create contract", func() {
   233  
   234  			// get chaincode invoke response payload and expect returned payload is serialized instance of some structure
   235  			contractCreateResponse := expectcc.PayloadIs(
   236  				cc.Invoke(
   237  					// invoke chaincode function
   238  					`contract_create`,
   239  					// with ContractDTO payload, it will automatically will be marshalled to json
   240  					&Contract1,
   241  				),
   242  				// We expect than payload response is marshalled ContractCreateResponse structure
   243  				&ContractCreateResponse{}).(ContractCreateResponse)
   244  
   245  			Expect(contractCreateResponse.Username).To(Equal(Contract1.Username))
   246  		})
   247  
   248  		It("Allow every one to get user info", func() {
   249  
   250  			// orininally was error https://github.com/IBM/build-blockchain-insurance-app/pull/44
   251  			user := expectcc.PayloadIs(
   252  				cc.Invoke(`user_get_info`, &GetUserDTO{
   253  					Username: Contract1.Username,
   254  				}), &ResponseUserDTO{}).(ResponseUserDTO)
   255  
   256  			Expect(user.LastName).To(Equal(Contract1.LastName))
   257  		})
   258  
   259  	})
   260  ```