Solidity Tests – How to Write Solidity Tests for Array Length & Contents Using Chai

chaihardhatsolidity

tdlr; solution

I had to await both of the values used in my assertions. For grabbing the length of the array, I needed to add a new method to my contract, as shown here. The reason for this is in the accepted answer.

function getPeopleSize() public view returns (uint256) {
     return people.length;
}

And here is the passing it block:

it("should add a person and their favorite number", async () => {
  const expectedValue = "11";
  const expectedName = "John";
  const transactionResponse = await simpleStorage.addPerson(
    expectedName,
    expectedValue
  );
  transactionResponse.wait(6);

  const johnValue = await simpleStorage.nameToFavoriteNumber(expectedName);
  const peopleSize = await simpleStorage.getPeopleSize();

  assert.equal(peopleSize, 1);
  assert.equal(johnValue, expectedValue);
});

I have a function addPerson, which stores a new name and favorite number:

function addPerson(string memory _name, uint256 _favoriteNumber) public {
    people.push(People(_favoriteNumber, _name));
    nameToFavoriteNumber[_name] = _favoriteNumber;
}

people is an array and favoriteNumber is a uint256. Both are public data types.

uint256 favoriteNumber;

struct People {
  uint256 favoriteNumber;
  string name;
}

People[] public people;

mapping(string => uint256) public nameToFavoriteNumber;

In my deployed Remix contract, I'm able to call, for example, nameToFavoriteNumber with the string of the user I've added, and the supplied number is returned as expected. However, I'm unsure how to write a test for the addPerson function, to ensure that the name and favorite number are indeed getting stored.

Here's what I've tried. The length of people is printing zero and the mapper prints undefined. I'm expecting it to pass with length equal to 1 and the mapper equal to 11. One final note is that my other tests are passing, so I'm certain that the simpleStorage contract deployed properly in my testing environment.

it("should add a person and their favorite number", async () => {
    const expectedValue = "11";
    const expectedName = "John";
    const transactionResponse = await simpleStorage.addPerson(
      expectedName,
      expectedValue
    );
    transactionResponse.wait(6);

    const people = simpleStorage.people;

    const johnValue = simpleStorage.nameToFavoriteNumber[expectedName];

    assert.equal(people.length, 1);         // prints 0
    assert.equal(johnValue, expectedValue); // prints undefined
});

Best Answer

Somehow I think is missing the keyword await to wait for the contract method promise to be resolved and the () in the end of each method.

Check to see if it helps.

it("should add a person and their favorite number", async () => {
    const expectedValue = "11";
    const expectedName = "John";
    const transactionResponse = await simpleStorage.addPerson(
      expectedName,
      expectedValue
    );

    // added await
    await transactionResponse.wait(6);

    // added await    
    // added () in the end (its a method)    
    // solidity retrieves only the value inside the position you pass. You must create a getPeopleSize method inside the contract to retrieve all length.
    const people = await simpleStorage.people(some index);

    // added await    
    // added () in the end (its a method)    
    const johnValue = await simpleStorage.nameToFavoriteNumber(expectedName);

    assert.equal(people.length, 1);
    assert.equal(johnValue, expectedValue);
});