How do I BEST test my ERC721 smart contract before deployment?

The simple intention of my smart contract is to be able to give away an NFT (from your wallet) as a gift. I added a function to schedule when the recipient receives the NFT gift.
I achieved this by manipulating the time / block.timestamp when the code below in my contract is allowed to run

    _transfer( from,  to,  tokenId );

Although I think the smart contract is ready for deployment/production, am not sure if I am using the BEST testing approach to verify that it is ready for production deployment.

I am hoping someone can point out a better/best testing practice for my contract.

Find below my smart contract code, followed by my step-by-step testing approach.
The idea is that you follow my my step-by-step testing approach and keep referring back to the code below:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts@4.4.2/token/ERC721/ERC721.sol";

contract etherGifter is ERC721 {

address private to;
address private from;
string  private messageToRecipient; 
uint256 public  giftCount = 0;
address private owner;
uint256 private tokenId = 1;
uint256 private firstReminderAt; 
uint256 private createdAt;

enum Status { recipientAssigned, firstReminderSent, secondReminderSent, giftGifted }

struct CryptoGift {
    address from;
    address to;
    uint256 tokenId;
    string  tokenName;
    string  messageToRecipient;
    Status  GiftStatus;
    uint256 createdAt;
    uint256 firstReminderAt;
mapping(address => CryptoGift) public gifts;

constructor() ERC721("Crypto Gifter", "CGFT") {
    owner = msg.sender; 


function mintNFTtoken() public {        
    _safeMint(msg.sender, tokenId);

    tokenId ++;

function setGiftRecipient( address _to, string calldata _tokenName, uint256  _tokenId, string calldata _messageToRecipient ) public payable { 
    gifts[msg.sender] = CryptoGift(msg.sender, _to, _tokenId, _tokenName, _messageToRecipient, Status.recipientAssigned, block.timestamp, block.timestamp +30 );

function getGiftStatus() public view returns ( Status) {
    CryptoGift storage giftStatus = gifts[msg.sender];
    return giftStatus.GiftStatus;

function processGifting() public payable {
    CryptoGift storage giftRecipient = gifts[msg.sender];
    uint256 isExecutionDate = giftRecipient.firstReminderAt;

    require( block.timestamp >= isExecutionDate, "WAIT... Its Not yet Valentines Day!" );

    from = giftRecipient.from;
    to =;
    tokenId = giftRecipient.tokenId;

    _transfer( from,  to,  tokenId );
    CryptoGift storage giftStatus = gifts[msg.sender];
    giftStatus.GiftStatus = Status.giftGifted;



The intention of my smart contract ISNT to mint any NFT tokens, however for testing purposes, I have had to add an NFT minting function. Needless to say, I will remove this function before deployment.


Because without any NFT's in my testing environment, I wouldn't have been able to test my smart contract.

In Remix, I deploy in the JavaScript VM Environment
using account: 0x5B3...eddC4 as the default wallet.

I mint 3 NFT tokens using the below function:


Running the Zeppelin ERC721 inherited function balanceOF() function confirms that 3 NFT tokens were successfully minted.

Also running Zeppelin ERC721 inherited function ownerOf() function confirms that NFT tokenId 1 is owned by the default wallet.

I then decide to choose the gift recipient by running my function


for recipient parameter: _to, I choose the wallet address: 0xAb...5cb2
token Name parameter: _tokenName, I write : None
for the tokenId parameter: _tokenId, I choose : 1
for the message parameter: _messageToRecipient, I write: Happy Valentines Day!

Ensuring that I am running the setGiftRecipient() as the contract owner:

I run my function below:


To verify that everything is in order, I run:

Excited that everything is working as designed, I now choose to process (the transfer of the gift) the gifting, by running:


Because I set the timer to 30 seconds, I first get the below message:

The transaction has been reverted to the initial state.
Reason provided by the contract: "WAIT... Its Not yet Valentines Day!".

I wait a few seconds later, then I run:


...and get an indication that the transaction was successful

I also check to varify new ownership of token number 1, by running Zeppelin ERC721 inherited function ownerOf() function.

My testing approach verifies that:

  1. The scheduling of the NFT transfers work,
  2. Also that the NFT transfers from an original owner to a new owner also works.

Is there a better method to run tests on my Dapp?