I am trying to add unit tests to my chaincode using Jest. From the sample repo here, it is using Sinon
to handle the mocking of ChaincodeStub
using createStubInstance
. I am looking to remove Sinon
dependency and handle the mocking part using Jest.
So far I have been trying:
const { ChaincodeStub } = require('fabric-shim');
const MyContract = require('./myContract');
describe('Asset Transfer Basic Tests', () => {
let transactionContext;
let mockChaincode;
let asset;
beforeEach(() => {
transactionContext = new Context();
mockChaincode = ChaincodeStub;
jest.mock('fabric-shim', () => ({
ChaincodeStub: jest.fn().mockImplementation(() => ({
deleteState: jest.fn().mockImplementation(async (key) => {
if (mockChaincode.states) {
delete mockChaincode.states[key];
}
return Promise.resolve(key);
}),
getState: jest.fn().mockImplementation(async (key) => {
let ret;
if (mockChaincode.states) {
ret = mockChaincode.states[key];
}
return Promise.resolve(ret);
}),
getStateByRange: jest.fn().mockImplementation(async () => {
function* internalGetStateByRange() {
if (mockChaincode.states) {
// Shallow copy
const copied = { ...mockChaincode.states };
for (const key in copied) {
yield { value: copied[key] };
}
}
}
return Promise.resolve(internalGetStateByRange());
}),
putState: jest.fn().mockImplementation((key, value) => {
if (!mockChaincode.states) {
mockChaincode.states = {};
}
mockChaincode.states[key] = value;
}),
})),
}));
transactionContext.setChaincodeStub(mockChaincode);
asset = {
birthDay: '1966-05-31T00:00:00.000Z',
firstName: 'Federico',
gender: 'male',
id: '09c2f565-9923-4b78-bd1c-ff635a70a880',
lastName: 'Villegas',
};
});
describe('Test InitLedger', (done) => {
it('should return error on InitLedger', async () => {
mockChaincode.putState.rejects('failed inserting key');
const myContract = new MyContract();
try {
await myContract.initLedger(transactionContext);
done.fail('initLedger should have failed');
} catch (err) {
expect(err.name).toBe('failed inserting key');
}
});
it('should return success on InitLedger', async () => {
const myContract = new MyContract();
await myContract.initLedger(transactionContext);
const ret = JSON.parse(
(
await mockChaincode.getState(
'09c2f565-9923-4b78-bd1c-ff635a70a880',
)
).toString(),
);
expect(ret).toEqual({ ...asset, docType: 'user' });
});
});
});
but so far what I am getting is the following error: TypeError: ctx.stub.putState is not a function
.
Might be missing something there.
Is there also something simpler like the createStubInstance
provided by Sinon
in Jest
?
After looking into the link sent by @TSN in the comment, I solved it using the following approach:
jest.doMock('fabric-shim');
const { ChaincodeStub } = require('fabric-shim');
const MyContract = require('./myContract');
describe('Asset Transfer Basic Tests', () => {
let transactionContext;
let chaincodeStub;
let asset;
beforeEach(() => {
transactionContext = new Context();
chaincodeStub = new ChaincodeStub();
transactionContext.setChaincodeStub(chaincodeStub);
chaincodeStub.putState = jest.fn((key, value) => {
if (!chaincodeStub.states) {
chaincodeStub.states = {};
}
chaincodeStub.states[key] = value;
});
chaincodeStub.getState = jest.fn(async (key) => {
let ret;
if (chaincodeStub.states) {
ret = chaincodeStub.states[key];
}
return Promise.resolve(ret);
});
chaincodeStub.deleteState = jest.fn(async (key) => {
if (chaincodeStub.states) {
delete chaincodeStub.states[key];
}
return Promise.resolve(key);
});
chaincodeStub.getStateByRange = jest.fn(async () => {
function* internalGetStateByRange() {
if (chaincodeStub.states) {
// Shallow copy
const copied = { ...chaincodeStub.states };
for (const key in copied) {
yield { value: copied[key] };
}
}
}
return Promise.resolve(internalGetStateByRange());
});
asset = {
birthDay: '1966-05-31T00:00:00.000Z',
firstName: 'Federico',
gender: 'male',
id: '09c2f565-9923-4b78-bd1c-ff635a70a880',
lastName: 'Villegas',
};
});
describe('Test initLedger', (done) => {
it('should return error on initLedger', async () => {
chaincodeStub.putState = () =>
Promise.reject(new Error('failed inserting key'));
const myContract = new MyContract();
try {
await myContract.initLedger(transactionContext);
done.fail('initLedger should have failed');
} catch (err) {
expect(err.message).toBe('failed inserting key');
}
});
it('should return success on initLedger', async () => {
const myContract = new MyContract();
await myContract.initLedger(transactionContext);
const ret = JSON.parse(
(
await chaincodeStub.getState(
'09c2f565-9923-4b78-bd1c-ff635a70a880',
)
).toString(),
);
expect(ret).toEqual({ ...asset, docType: 'user' });
});
});
});