測試和除錯Convector智慧合約

買賣虛擬貨幣

在開發智慧合約的同時,避免浪費時間的任務是明智的。使用Convector,您可以使用Mock分類帳執行本地單元測試,您也可以使用它來除錯程式碼。使用最新版本的Hurley和Convector 智慧合約,現在甚至可以直接從區塊鏈進行除錯。

單元測試

什麼是單元測試?

通常,您會獲取一段程式碼單元來進行測試(一個模組、一個函式、一組函式)並編寫呼叫該函式並檢查結果的函式。這些測試可以在編譯、整合或部署程式碼時執行,這樣您就可以確保預期發生的事情仍然會發生。

整體單元測試可以為您節省大量時間和金錢,同時還可以幫助您保持健康的程式碼庫。

如何操作

首先安裝Convector-CLI, 一定要有Node 8.11.0和Docker Community Edition。

確保您的是Convector-CLI 1.1.0版

npm i -g @worldsibu/convector-cli
現在,讓我們從建立一個新的Convector專案開始:

$ conv new bughunting -c mybuggychaincode
$cd bughunting
$ npm install

測試

預設情況下,為./packages/mybuggychaincode/tests/mybuggychaincode.spec.ts中的鏈程式碼建立了一個測試檔案。

我們使用mocha進行測試,這是一個著名的lib。 該檔案只是帶來管道並測試控制器具有的預設建立功能。

 在你的終端執行npm run test

 您剛剛為Convector智慧合約進行了第一次單元測試。 這也是您的CI / CD過程中自動執行的內容。

mocha應用相當廣泛,您可以在這裡檢視所有選項。 現在,讓我們建立一個額外的功能並稍微改變智慧合約。

相應地更新以下檔案mybuggychaincode.controller.ts,mybuggychaincode.model.ts和mybuggychaincode.spec.ts:

import {
  Controller,
  ConvectorController,
  Invokable,
  Param
from'@worldsibu/convector-core';
import * as yup from'yup';
import { Mybuggychaincode } from'./mybuggychaincode.model';

@Controller('mybuggychaincode')
exportclassMybuggychaincodeControllerextendsConvectorController{

  @Invokable()
  public async create(
    @Param(Mybuggychaincode)
    mybuggychaincode: Mybuggychaincode
  ) {
console.log(`sender=${this.sender}`);
    mybuggychaincode.owner = this.sender;
await mybuggychaincode.save();
return mybuggychaincode;
  }

  @Invokable()
  public async getOne(
    @Param(yup.string())
    id: string
  ) {
returnawait Mybuggychaincode.getOne(id);
  }

  @Invokable()
  public async update(
    @Param(Mybuggychaincode)
    mybuggychaincode: Mybuggychaincode
  ) {
let existingModel = await Mybuggychaincode.getOne(mybuggychaincode.id);

if (!existingModel.id) {
thrownewError(`Item with ${mybuggychaincode.id} doesn't exist in the blockchain!`);
    }
console.log(existingModel);
console.log(`${existingModel.owner}${this.sender}`)
if (existingModel.owner !== this.sender) {
thrownewError(`Ups, the requesting identity is not authorized to update the model`);
    }

// Make changes
    existingModel.name = mybuggychaincode.name;

await existingModel.save();
console.log(existingModel);
return existingModel;
  }
}

mybuggychaincode.controller.ts

import * as yup from 'yup';
import {
  ConvectorModel,
  Default,
  ReadOnly,
  Required,
  Validate
} from '@worldsibu/convector-core';

export classMybuggychaincodeextendsConvectorModel<Mybuggychaincode{
@ReadOnly()
@Required()
public readonly type = 'io.worldsibu.mybuggychaincode';

@Required()
@Validate(yup.string())
public name: string;

@Validate(yup.string())
public owner: string;

@ReadOnly()
@Required()
@Validate(yup.number())
public created: number;

@Required()
@Validate(yup.number())
public modified: number;
}

mybuggychaincode.model.ts 

// tslint:disable:no-unused-expression
import { join } from'path';
import * as chai from'chai';
import { expect } from'chai';
import * as uuid from'uuid/v4';
import { MockControllerAdapter } from'@worldsibu/convector-adapter-mock';
import'mocha';
import * as chaiAsPromised from'chai-as-promised';
import { Mybuggychaincode } from'../src/mybuggychaincode.model';
import { ChaincodeMockStub } from'@theledger/fabric-mock-stub';
import { MybuggychaincodeController } from'../src';
import { ClientFactory } from'@worldsibu/convector-core';

describe('Mybuggychaincode'async () => {
    chai.use(chaiAsPromised);
let modelSample: Mybuggychaincode;
let adapter: MockControllerAdapter;
let mybuggychaincodeCtrl: MybuggychaincodeController;

let modelId = uuid();
// By default, MockControllerAdapter will use this fingerprint as the `this.sender`
const mockIdentity = 'B6:0B:37:7C:DF:D2:7A:08:0B:98:BF:52:A4:2C:DC:4E:CC:70:91:E1';

    before(async () => {
const now = newDate().getTime();
        modelSample = new Mybuggychaincode();
        modelSample.id = modelId;
        modelSample.name = 'Test';
        modelSample.created = now;
        modelSample.modified = now;
// Mocks the blockchain execution environment
        adapter = new MockControllerAdapter();
await adapter.init([
            {
version'*',
controller'MybuggychaincodeController',
name: join(__dirname, '..')
            }
        ]);
        mybuggychaincodeCtrl = ClientFactory(MybuggychaincodeController, adapter);
    });

    it('should create a default model'async () => {
await mybuggychaincodeCtrl.create(modelSample);

const justSavedModel = await adapter.getById<Mybuggychaincode>(modelSample.id);

        expect(justSavedModel.id).to.exist;
    });

    it('should find the model'async () => {
let result = new Mybuggychaincode(
await mybuggychaincodeCtrl
                .getOne(modelId));
console.log(result);
        expect(result.id).to.exist;
    });

    it('try to update without success'async () => {
// Fake a different identity sending the transaction
        (adapter.stub as any).usercert = '-----BEGIN CERTIFICATE-----' +
'MIICjzCCAjWgAwIBAgIUITsRsw5SIJ+33SKwM4j1Dl4cDXQwCgYIKoZIzj0EAwIw' +
'czELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh' +
'biBGcmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMT' +
'E2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMTgwODEzMDEyOTAwWhcNMTkwODEzMDEz' +
'NDAwWjBCMTAwDQYDVQQLEwZjbGllbnQwCwYDVQQLEwRvcmcxMBIGA1UECxMLZGVw' +
'YXJ0bWVudDExDjAMBgNVBAMTBXVzZXIzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD' +
'QgAEcrfc0HHq5LG1UbyPSRLNjIQKqYoNY7/zPFC3UTJi3TTaIEqgVL6DF/8JIKuj' +
'IT/lwkuemafacXj8pdPw3Zyqs6OB1zCB1DAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0T' +
'AQH/BAIwADAdBgNVHQ4EFgQUHFUlW/XJC7VcJe5pLFkz+xlMNpowKwYDVR0jBCQw' +
'IoAgQ3hSDt2ktmSXZrQ6AY0EK2UHhXMx8Yq6O7XiA+X6vS4waAYIKgMEBQYHCAEE' +
'XHsiYXR0cnMiOnsiaGYuQWZmaWxpYXRpb24iOiJvcmcxLmRlcGFydG1lbnQxIiwi' +
'aGYuRW5yb2xsbWVudElEIjoidXNlcjMiLCJoZi5UeXBlIjoiY2xpZW50In19MAoG' +
'CCqGSM49BAMCA0gAMEUCIQCNsmDjOXF/NvciSZebfk2hfSr/v5CqRD7pIHCq3lIR' +
'lwIgPC/qGM1yeVinfN0z7M68l8rWn4M4CVR2DtKMpk3G9k9=' +
'-----END CERTIFICATE-----';

await expect(mybuggychaincodeCtrl.update(modelSample)).to.be.eventually
            .rejectedWith('Ups, the requesting identity is not authorized to update the model');
    });

    it('try to update with success'async () => {
// Fake to the correct identity again
        adapter.stub['fingerprint'] = mockIdentity;

await expect(async () => await mybuggychaincodeCtrl.update(modelSample)
            .then((result) => expect(result.id).to.exist,
                (ex) => expect.fail('Should not have failed')));
    });
});

mybuggychaincode.spec.ts

繼續進行測試:

$ npm test

4個測試功能應該已成功執行!

除錯

除錯是另一個有用的任務,使用Convector Suite可以除錯單元測試,但也可以除錯在區塊鏈中執行的程式碼

除錯單元測試

轉到./packages/mybuggychaincode-cc/package.json並使用以下內容替換測試任務的內容。

"test""npm run build && mocha --inspect --debug=8888 -r ts-node/register tests/*.spec.ts --reporter spec"

--inspect param將允許您連線偵錯程式並遍歷您的程式碼。 我還更改了埠以避免與其他程序發生衝突。

您可以使用首選的偵錯程式。我喜歡在節點使用chrome devtools,所以我只需在瀏覽器中訪問chrome://inspect並單擊開啟節點專用devtools。

單擊Add connection並輸入localhost:8888以偵聽該埠。

回到您的程式碼,並在位於./packages/mybuggychaincode/tests/mybuggychaincode.spec.ts中的測試檔案中,將偵錯程式放在某處。

執行npm test! 您現在應該能夠很順暢地除錯您的程式碼。

在區塊鏈中除錯程式碼

我們使用Hurley,從1.0.0開始它現在支援除錯引數。

您將在CLI中需要兩個新任務(如果您還沒有它們)。 轉到你的root package.json並查詢cc:start:debug和cc:install:debug。

npm run cc:start:debug — mybuggychaincode

如果你看到這個,它就準備好進行除錯了。

在9990中監聽除錯埠。

您現在需要做的就是對要配置的容器和載入的程式碼進行虛擬呼叫。

# It will throw an expected error - This will take some seconds while downloading dependencies again in the rest of the organizations
$ hurl invoke mybuggychaincode init

現在轉到DevTools並探索原始碼。

您將看到正在執行的本機程式碼:

在此處查詢完整的原始碼:https://github.com/worldsibu/convector-example-unit-tests

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;