如何为 getHistoryForKey 方法生成模拟对象?

How To Generate Mocked Object For getHistoryForKey method?

我正在尝试构建存根对象来测试我用 typescript 编写的 hyperledger fabric 链代码。

我对存根感兴趣的方法是 getHistoryForKey 方法,它的 return 类型为 Promise<Iterators.HistoryQueryIterator> & AsyncIterable<Iterators.KeyModification>

有人可以告诉我如何为此生成存根对象吗?

我的代码:

chai.should();
chai.use(chaiAsPromised);
chai.use(sinonChai);

class TestContext implements Context {
  public stub: sinon.SinonStubbedInstance<ChaincodeStub> = sinon.createStubInstance(ChaincodeStub);
  public clientIdentity: sinon.SinonStubbedInstance<ClientIdentity> = sinon.createStubInstance(ClientIdentity);
  public logger = {
    getLogger: sinon.stub().returns(sinon.createStubInstance(winston.createLogger().constructor)),
    setLevel: sinon.stub(),
  };
}

describe('MyContract', () => {
  let ctx: TestContext;
  let stubAsyncIterator: AsyncIterable<Iterators.KeyModification> & Iterators.HistoryQueryIterator;

  beforeEach(() => {
    ctx = new TestContext();
    stubAsyncIterator = {
      async *[Symbol.asyncIterator]() {
           yield { isDelete: false, value: Buffer.from('{"entityID":"1000","data":{}}'), timestamp: { seconds: 123, nanos: 111 } as Timestamp, txId: 'txid' } as Iterators.KeyModification;
      },
    };
    ctx.stub.getHistoryForKey.withArgs('1000').resolves(stubAsyncIterator);
  });

...

});

此错误为:

error TS2322: Type '{ [Symbol.asyncIterator](): AsyncGenerator<KeyModification, void, undefined>; }' is not assignable to type 'AsyncIterable<KeyModification> & HistoryQueryIterator'.
  Type '{ [Symbol.asyncIterator](): AsyncGenerator<KeyModification, void, undefined>; }' is missing the following properties from type 'CommonIterator<KeyModification>': close, next

这是一个非常艰难的过程。发现仅调用原始代码并模拟某些函数比尝试手动重新创建 getHistoryForKey 对象更容易。所有这些代码的灵感来自于 unit tests.

代码如下:

import { Context } from 'fabric-contract-api';
import { ChaincodeStub, ClientIdentity, Iterators } from 'fabric-shim';
import { HistoryQueryIterator } from 'fabric-shim/lib/iterators';
import { ChaincodeMessageHandler } from 'fabric-shim/lib/handler';

chai.should();
chai.use(chaiAsPromised);
chai.use(sinonChai);

// test context updated to match fabric-contract-api version 2.4.1
class TestContext implements Context {
  public stub: sinon.SinonStubbedInstance<ChaincodeStub> = sinon.createStubInstance(ChaincodeStub);
  public clientIdentity: sinon.SinonStubbedInstance<ClientIdentity> = sinon.createStubInstance(ClientIdentity);
  public logging = {
    getLogger: sinon.stub().returns(sinon.createStubInstance(winston.createLogger().constructor)),
    setLevel: sinon.stub(),
  };
}

describe('MyContract', () => {

  let ctx: TestContext;
  let mockHandler: ChaincodeMessageHandler;
  let mockResponse: { results: any, hasMore: boolean };
  let sandbox;

  beforeEach(() => {
    ...

    sandbox = sinon.createSandbox();
    mockHandler = sandbox.createStubInstance(ChaincodeMessageHandler);

    // we're not actually processing the results, so just fill the results array with the number of items you want to simulate returning in history
    mockResponse = {results: [1,2,3,4,5], hasMore: false};

    const iterator = new HistoryQueryIterator(mockHandler, 'TestChannelID','txId',mockResponse);

    const getResultsFromBytesStub = sinon.stub(iterator, '_getResultFromBytes');

    // mocking out the actual responses returned with each iteration
    getResultsFromBytesStub.onCall(0).returns({ value: Buffer.from('{"id":"1000","data":{"value": 1}}')});
    getResultsFromBytesStub.onCall(1).returns({ value: Buffer.from('{"id":"1000","data":{"value": 2}}')});
    getResultsFromBytesStub.onCall(2).returns({ value: Buffer.from('{"id":"1000","data":{"value": 3}}')});
    getResultsFromBytesStub.onCall(3).returns({ value: Buffer.from('{"id":"1000","data":{"value": 4}}')});
    getResultsFromBytesStub.onCall(4).returns({ value: Buffer.from('{"id":"1000","data":{"value": 5}}')});


    ctx.stub.getHistoryForKey.withArgs('1000').resolves(iterator);
  });

...