Skip to content

Unit Testing the ERC721 Token

As always, we need to add Unit Tests. Without going too much into the unit-testing details. The tests are doing essentially the same as we did manually here and will come in handy in the next part, where we are optimizing for gas cost and where we customize the Token. Essentially we don't need the burn and pause functionality at all, and we can also make it a bit more lightweight.

Install truffle-assertions

On a regular terminal (exit the truffle developer console if you are still in there), we need to add another npm package.

Install truffle assertions:

npm install truffle-assertions

Adding a Unit Test

Then add the following unit tests to /test/lockUnlocking.test.js

const AisthisiToken = artifacts.require('AisthisiToken');
const truffleAssert = require('truffle-assertions');

let correctUnlockCode = web3.utils.sha3('test'); //test is the password
let timestampLockedFrom = Math.round(Date.now() / 1000) + 3; //lock it in 3 seconds to test unlock
let unlockCodeHash = web3.utils.sha3(correctUnlockCode); //double hashed

contract('AisthisiToken: test mint and lock', (accounts) => {
    const [deployerAddress, tokenHolderOneAddress, tokenHolderTwoAddress] = accounts;

    it('is possible to mint tokens for the minter role', async () => {
        let token = await AisthisiToken.deployed();

        await token.mint(tokenHolderOneAddress, timestampLockedFrom, unlockCodeHash); //minting works
        await truffleAssert.fails(token.transferFrom(deployerAddress, tokenHolderOneAddress, 0)); //transferring for others doesn't work

        //but transferring in general works
        await truffleAssert.passes(
            token.transferFrom(tokenHolderOneAddress, tokenHolderTwoAddress, 0, { from: tokenHolderOneAddress }),
        );
    });

    it('is not possible to transfer locked tokens', async () => {
        //unless we wait 4 seconds and the token will be locked
        let token = await AisthisiToken.deployed();
        await new Promise((res) => {
            setTimeout(res, 4000);
        });
        await truffleAssert.fails(
            token.transferFrom(tokenHolderTwoAddress, tokenHolderOneAddress, 0, { from: tokenHolderTwoAddress }),
            truffleAssert.ErrorType.REVERT,
            'AishtisiToken: Token locked',
        );
    });

    it('is not possible to unlock tokens for anybody else than the token holder', async () => {
        let token = await AisthisiToken.deployed();
        await truffleAssert.fails(
            token.unlockToken(correctUnlockCode, 0, { from: deployerAddress }),
            truffleAssert.ErrorType.REVERT,
            'AishtisiToken: Only the Owner can unlock the Token',
        );
    });

    it('is not possible to unlock tokens without the correct unlock code', async () => {
        let token = await AisthisiToken.deployed();
        let wrongUnlockCode = web3.utils.sha3('Santa Lucia');
        await truffleAssert.fails(
            token.unlockToken(wrongUnlockCode, 0, { from: tokenHolderTwoAddress }),
            truffleAssert.ErrorType.REVERT,
            'AishtisiToken: Unlock Code Incorrect',
        );
    });

    it('is possible to unlock the token and transfer it again', async () => {
        let token = await AisthisiToken.deployed();
        await truffleAssert.passes(token.unlockToken(correctUnlockCode, 0, { from: tokenHolderTwoAddress }));
        await truffleAssert.passes(
            token.transferFrom(tokenHolderTwoAddress, deployerAddress, 0, { from: tokenHolderTwoAddress }),
        );
        let tokenOwner = await token.ownerOf(0);
        assert.equal(tokenOwner, deployerAddress, 'The Owner is not the correct address');
    });

    it('is possible to retrieve the correct token URI', async () => {
        let token = await AisthisiToken.deployed();
        let metadata = await token.tokenURI(0);
        assert.equal('https://aisthisi.art/metadata/0.json', metadata);
    })
});

So, what's inside the tests?

  • is possible to mint tokens for the minter role tests that only an account with the "minter" role can mint tokens
  • is not possible to transfer locked tokens tests that locked tokens can really not be transferred
  • is not possible to unlock tokens for anybody else than the token holder tests that only the NFT holder can actually unlock the tokens
  • is not possible to unlock tokens without the correct unlock code tests that you can not unlock the token with an incorrect code
  • is possible to unlock the token and transfer it again tests that you can unlock the token with the correct code and then send it to someone else again
  • is possible to retrieve the correct token URI tests that we are actually getting the URI back that we expect

Running the Unit Test

Then simply run truffle test:

I personally like going through tests, because they tell me what the Author of Smart Contracts had in mind. They are usually quite easy to read. I hope ours are easy enough to understand what's going on under the hood.