4

I want to write a test that matches the input of creating NFT token. The input I have is a few strings and I put them inside a struct.

struct TokenMetadata {
        string _tokenURI;
        string _eventName;
    }

function tokenMetadata(uint256 tokenId) public view returns (TokenMetadata memory) 

When I am writing test for this method I can't parse the returned type. How should I write tests like this using Foundry?

4 Answers 4

1

Found a way with using library. In the smart contract file I put a library with struct inside import it in the test file and use it that way.

1
1

Presumably you want to compare the returned struct with some other struct in Foundry? To do so you can serialize the struct to bytes and then compare the serialized bytes. This way, you can validate that the returned struct matches the expected output.

You can use forge-std/assertEq function to compare 2 bytes for equivalence.

You should use abi.encode(yourStructVariable) to serialize it to bytes. NB: abi.encode() is used instead of abi.encodePacked() for better type safety. This ensures each data piece maintains its integrity, making it easier for debugging and minimizing the risk of data collision.

The following is an example on how this can be done in your particular case:

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;

import { Test, console2 } from "forge-std/Test.sol";

contract DummyTokenMetadataTest is Test {

    struct TokenMetadata {
        string _tokenURI;
        string _eventName;
    }

    function testTokenMetadata() public {
        TokenMetadata memory expected = TokenMetadata("dummy uri", "dummy name");
        TokenMetadata memory actual = _tokenMetadata(0);
        assertEq(expected, actual);
    }

    // implement a function that accepts 2 TokenMetadata variables to compare
    function assertEq(TokenMetadata memory expected, TokenMetadata memory actual) internal {
        bytes memory expectedBytes = abi.encode(expected);
        bytes memory actualBytes = abi.encode(actual);
        assertEq(expectedBytes, actualBytes); // use the forge-std/Test assertEq(bytes, bytes) function
    }

    function _tokenMetadata(uint256 tokenId) internal pure returns (TokenMetadata memory tokenMetadata) {
        if (tokenId == 0) {
            tokenMetadata._tokenURI = "dummy uri";
            tokenMetadata._eventName = "dummy name";
        }
    }
}
0

In your statement, the "input" is the uint256 tokenId parameter.

It seems you'd like to compare the output of your function with a struct of your choice.

If so, you'd go about it this way. You've noted that the assert functions do not allow to pass a struct argument.

https://book.getfoundry.sh/reference/forge-std/assertEq

  1. Create a function that compares the keccak256 values of two or more structs.

  2. Create the struct you'd like to compare it to, with the relevant values.

  3. Use assertTrue to verify if the bool is true, or false.

function equals(TokenMetadata memory _first, TokenMetadata memory _second) internal view returns (bool) {
        // Just compare the output of hashing all fields packed
        return(keccak256(abi.encodePacked(_first._tokenURI, _first._eventName)) == keccak256(abi.encodePacked(_second._tokenURI, _second._eventName)));
    }

TokenMetadata memory _newtokenMetadata;
_newtokenMetadata._tokenURI = "URIofChoice";
_newtokenMetadata._eventName = "EventName";

assertTrue(equals(tokenMetadata(tokenId), _newtokenMetadata));
0

Testing a function that returns a struct, especially when dealing with strings, can be a bit tricky in Solidity. However, you can write a test using Foundry by creating a helper function to compare the structs and using the testing utilities provided by Foundry.

Here's an example of how you can write a test for the tokenMetadata function.

  1. Create a Helper Function to Compare Structs:

You can create an internal function to compare the TokenMetadata structs. You'll need to compare each field of the structs.

function isEqual(TokenMetadata memory a, TokenMetadata memory b) internal pure returns(bool) {
    return (keccak256(abi.encodePacked(a._tokenURI)) == keccak256(abi.encodePacked(b._tokenURI))) && 
           (keccak256(abi.encodePacked(a._eventName)) == keccak256(abi.encodePacked(b._eventName)));
}
  1. Write the Test:

You can then use this helper function in your test to compare the expected TokenMetadata with the one returned by the tokenMetadata function.

function test_TokenMetadata() public {
    // Assuming you have created the token with the given tokenId
    uint256 tokenId = 1; 
    TokenMetadata memory expectedMetadata = TokenMetadata("https://example.com/tokenURI", "Event Name");
    
    // Assuming you have set the metadata for the token in the contract
    TokenMetadata memory actualMetadata = yourContract.tokenMetadata(tokenId);

    assertTrue(isEqual(expectedMetadata, actualMetadata), "Token metadata does not match");
}

By following these steps, you can effectively write a test to compare the expected and actual TokenMetadata for your NFT token. Make sure to replace yourContract with the actual instance of your contract that you're testing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.