--- a
+++ b/contract/test/my-asset-contract.js
@@ -0,0 +1,108 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+'use strict';
+
+const { ChaincodeStub, ClientIdentity } = require('fabric-shim');
+const { MyAssetContract } = require('..');
+const winston = require('winston');
+
+const chai = require('chai');
+const chaiAsPromised = require('chai-as-promised');
+const sinon = require('sinon');
+const sinonChai = require('sinon-chai');
+
+chai.should();
+chai.use(chaiAsPromised);
+chai.use(sinonChai);
+
+class TestContext {
+
+    constructor() {
+        this.stub = sinon.createStubInstance(ChaincodeStub);
+        this.clientIdentity = sinon.createStubInstance(ClientIdentity);
+        this.logging = {
+            getLogger: sinon.stub().returns(sinon.createStubInstance(winston.createLogger().constructor)),
+            setLevel: sinon.stub(),
+        };
+    }
+
+}
+
+describe('MyAssetContract', () => {
+
+    let contract;
+    let ctx;
+
+    beforeEach(() => {
+        contract = new MyAssetContract();
+        ctx = new TestContext();
+        ctx.stub.getState.withArgs('1001').resolves(Buffer.from('{"value":"my asset 1001 value"}'));
+        ctx.stub.getState.withArgs('1002').resolves(Buffer.from('{"value":"my asset 1002 value"}'));
+    });
+
+    describe('#myAssetExists', () => {
+
+        it('should return true for a my asset', async () => {
+            await contract.myAssetExists(ctx, '1001').should.eventually.be.true;
+        });
+
+        it('should return false for a my asset that does not exist', async () => {
+            await contract.myAssetExists(ctx, '1003').should.eventually.be.false;
+        });
+
+    });
+
+    describe('#createMyAsset', () => {
+
+        it('should create a my asset', async () => {
+            await contract.createMyAsset(ctx, '1003', 'my asset 1003 value');
+            ctx.stub.putState.should.have.been.calledOnceWithExactly('1003', Buffer.from('{"value":"my asset 1003 value"}'));
+        });
+
+        it('should throw an error for a my asset that already exists', async () => {
+            await contract.createMyAsset(ctx, '1001', 'myvalue').should.be.rejectedWith(/The my asset 1001 already exists/);
+        });
+
+    });
+
+    describe('#readMyAsset', () => {
+
+        it('should return a my asset', async () => {
+            await contract.readMyAsset(ctx, '1001').should.eventually.deep.equal({ value: 'my asset 1001 value' });
+        });
+
+        it('should throw an error for a my asset that does not exist', async () => {
+            await contract.readMyAsset(ctx, '1003').should.be.rejectedWith(/The my asset 1003 does not exist/);
+        });
+
+    });
+
+    describe('#updateMyAsset', () => {
+
+        it('should update a my asset', async () => {
+            await contract.updateMyAsset(ctx, '1001', 'my asset 1001 new value');
+            ctx.stub.putState.should.have.been.calledOnceWithExactly('1001', Buffer.from('{"value":"my asset 1001 new value"}'));
+        });
+
+        it('should throw an error for a my asset that does not exist', async () => {
+            await contract.updateMyAsset(ctx, '1003', 'my asset 1003 new value').should.be.rejectedWith(/The my asset 1003 does not exist/);
+        });
+
+    });
+
+    describe('#deleteMyAsset', () => {
+
+        it('should delete a my asset', async () => {
+            await contract.deleteMyAsset(ctx, '1001');
+            ctx.stub.deleteState.should.have.been.calledOnceWithExactly('1001');
+        });
+
+        it('should throw an error for a my asset that does not exist', async () => {
+            await contract.deleteMyAsset(ctx, '1003').should.be.rejectedWith(/The my asset 1003 does not exist/);
+        });
+
+    });
+
+});
\ No newline at end of file