在开发智能合约的同时,避免浪费时间的任务是明智的。使用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')
export class MybuggychaincodeController extends ConvectorController {
@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
) {
return await Mybuggychaincode.getOne(id);
}
@Invokable()
public async update(
@Param(Mybuggychaincode)
mybuggychaincode: Mybuggychaincode
) {
let existingModel = await Mybuggychaincode.getOne(mybuggychaincode.id);
if (!existingModel.id) {
throw new Error(`Item with ${mybuggychaincode.id} doesn't exist in the blockchain!`);
}
console.log(existingModel);
console.log(`${existingModel.owner} ${this.sender}`)
if (existingModel.owner !== this.sender) {
throw new Error(`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 class Mybuggychaincode extends ConvectorModel<Mybuggychaincode> {
public readonly type = 'io.worldsibu.mybuggychaincode';
)
public name: string;
)
public owner: string;
)
public created: 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 = new Date().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