1

I am trying to check whether an event is emitted or not in my unit test. But getting error as length undefined.

The error I am getting is

TypeError: Cannot read properties of undefined (reading '0')
      at Context.<anonymous> (test/Institution-test.js:84:21)
      at processTicksAndRejections (node:internal/process/task_queues:95:5)

Here is my Institution.sol Contract

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

import "./Certification.sol";

error NotOwner();

contract Institution {
    // State Variables
    address public owner;

    // Mappings
    mapping(address => Institute) private institutes; // Institutes Mapping
    mapping(address => Course[]) private instituteCourses; // Courses Mapping

    // Events
    event instituteAdded(string indexed _instituteName);

    // Modifier
    modifier onlyOwner() {
        if (msg.sender != owner) revert NotOwner();
        _;
    }

    constructor() {
        owner = msg.sender;
    }

    struct Course {
        string course_name;
        string course_code;
        // Other attributes can be added
    }

    struct Institute {
        string institute_name;
        string institute_acronym;
        string institute_link;
    }

    function stringToBytes32(
        string memory source
    ) private pure returns (bytes32 result) {
        bytes memory tempEmptyStringTest = bytes(source);
        if (tempEmptyStringTest.length == 0) {
            return 0x0;
        }
        assembly {
            result := mload(add(source, 32))
        }
    }

    function addInstitute(
        address _address,
        string memory _institute_name,
        string memory _institute_acronym,
        string memory _institute_link,
        Course[] memory _institute_courses
    ) public onlyOwner {
        bytes memory tempEmptyStringNameTest = bytes(
            institutes[_address].institute_name
        );
        require(
            tempEmptyStringNameTest.length == 0,
            "Institute with token already exists"
        );
        require(
            _institute_courses.length > 0,
            "Atleast one course must be added"
        );
        institutes[_address] = Institute(
            _institute_name,
            _institute_acronym,
            _institute_link
        );
        for (uint256 i = 0; i < _institute_courses.length; i++) {
            instituteCourses[_address].push(_institute_courses[i]);
        }

        emit instituteAdded(_institute_name);
    }

    // Called by Institutions
    function getInstituteData()
        public
        view
        returns (string memory, string memory, string memory, Course[] memory)
    {
        Institute memory temp = institutes[msg.sender];
        bytes memory tempEmptyStringNameTest = bytes(temp.institute_name);
        require(
            tempEmptyStringNameTest.length > 0,
            "Institute account does not exist!"
        );
        return (
            temp.institute_name,
            temp.institute_acronym,
            temp.institute_link,
            instituteCourses[msg.sender]
        );
    }

    // Called by Smart Contracts
    function getInstituteData(
        address _address
    )
        public
        view
        returns (string memory, string memory, string memory, Course[] memory)
    {
        require(
            Certification(msg.sender).owner() == owner,
            "Incorrect smart contract & authorizations!"
        );
        Institute memory temp = institutes[_address];
        bytes memory tempEmptyStringNameTest = bytes(temp.institute_name);
        require(
            tempEmptyStringNameTest.length > 0,
            "Institute does not exist!"
        );
        return (
            temp.institute_name,
            temp.institute_acronym,
            temp.institute_link,
            instituteCourses[_address]
        );
    }

    function checkInstitutePermission(
        address _address
    ) public view returns (bool) {
        Institute memory temp = institutes[_address];
        bytes memory tempEmptyStringNameTest = bytes(temp.institute_name);
        if (tempEmptyStringNameTest.length > 0) {
            return true;
        } else {
            return false;
        }
    }
}

And here is my Institution-test.js

const web3 = require("web3");
const { assert, expect } = require("chai");
const { ethers } = require("hardhat");

describe("Institution Contract", function () {
  let mockOwner_acc,
    mockInstitute_acc,
    mockInvalid_acc,
    mockInstitute,
    mockInstitute_course,
    institution;

  beforeEach(async function () {
    const accounts = await ethers.getSigners();
    mockOwner_acc = accounts[0];
    mockInstitute_acc = accounts[1];
    mockInvalid_acc = accounts[2];

    mockInstitute = {
      institute_name: "Heritage Institute of Technology",
      institute_acronym: "HITK",
      institute_link: "www.heritageit.edu",
    };

    mockInstitute_course = [
      {
        course_name: "Computer Science and Engineering",
        course_code: "CSE",
      },
      {
        course_name: "Electronics and Communication Engineering",
        course_code: "ECE",
      },
      {
        course_name: "Computer Science and Business Studies",
        course_code: "CSBS",
      },
    ];

    const Institution = await ethers.getContractFactory("Institution");
    institution = await Institution.deploy({
      from: mockOwner_acc,
    });
  });

  describe("Deployment of Institution Contract", () => {
    it("It has correct owner", async function () {
      const instituteContract_owner = await institution.owner();
      assert.equal(instituteContract_owner, mockOwner_acc.address);
    });
  });

  describe("Adding institute", () => {
    it("Only owner can add Institute, others cannot add", async function () {
      const attackerConnectedAccounts = await institution.connect(
        mockInvalid_acc
      );
      await expect(
        attackerConnectedAccounts.addInstitute(
          mockInstitute_acc.address,
          mockInstitute.institute_name,
          mockInstitute.institute_acronym,
          mockInstitute.institute_link,
          mockInstitute_course
        )
      ).to.be.revertedWithCustomError(attackerConnectedAccounts, "NotOwner");
    });

    it("Adds an institute with valid details", async function () {
      const receipt = await institution.addInstitute(
        mockInstitute_acc.address,
        mockInstitute.institute_name,
        mockInstitute.institute_acronym,
        mockInstitute.institute_link,
        mockInstitute_course,
        { from: mockOwner_acc }
      );

      console.log(receipt);

      assert.equal(receipt.logs.length, 1);
      assert.equal(receipt.logs[0].event, "instituteAdded");
      assert.equal(
        receipt.logs[0].args._institute_name,
        mockInstitute.institute_name
      );
    });
  });
});

1 Answer 1

0

Considering that you are mocking a Contract with ethers (ethers.js), you'd need to write the addInstitution call differently:

const tx = await institution.addInstitute(
        mockInstitute_acc.address,
        mockInstitute.institute_name,
        mockInstitute.institute_acronym,
        mockInstitute.institute_link,
        mockInstitute_course,
        { from: mockOwner_acc }
      );

const { events, logs } = await tx.wait();

// Example tx.wait() return...
// {
//   blockHash: '0x9340a9c7efafbb4ad9a6ebda62e3311ec5fc40ddcc733632938a988c8c62b885',
//   blockNumber: 60330,
//   byzantium: true,
//   confirmations: 1,
//   contractAddress: null,
//   cumulativeGasUsed: { BigNumber: "51558" },
//   effectiveGasPrice: { BigNumber: "1500000007" },
//   events: [
//     {
//       address: '0x70ff5c5B1Ad0533eAA5489e0D5Ea01485d530674',
//       args: [
//         '0x46E0726Ef145d92DEA66D38797CF51901701926e',
//         '0x5555763613a12D8F3e73be831DFf8598089d3dCa',
//         { BigNumber: "1230000000000000000" },
//         amount: { BigNumber: "1230000000000000000" },
//         from: '0x46E0726Ef145d92DEA66D38797CF51901701926e',
//         to: '0x5555763613a12D8F3e73be831DFf8598089d3dCa'
//       ],
//       blockHash: '0x9340a9c7efafbb4ad9a6ebda62e3311ec5fc40ddcc733632938a988c8c62b885',
//       blockNumber: 60330,
//       data: '0x0000000000000000000000000000000000000000000000001111d67bb1bb0000',
//       decode: [Function (anonymous)],
//       event: 'Transfer',
//       eventSignature: 'Transfer(address,address,uint256)',
//       getBlock: [Function (anonymous)],
//       getTransaction: [Function (anonymous)],
//       getTransactionReceipt: [Function (anonymous)],
//       logIndex: 0,
//       removeListener: [Function (anonymous)],
//       topics: [
//         '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
//         '0x00000000000000000000000046e0726ef145d92dea66d38797cf51901701926e',
//         '0x0000000000000000000000005555763613a12d8f3e73be831dff8598089d3dca'
//       ],
//       transactionHash: '0xafdc1f7ec14e2e05a39826c134c9157e0011fc784aef623df3fdf9b9d29f3ac8',
//       transactionIndex: 0
//     }
//   ],
//   from: '0x46E0726Ef145d92DEA66D38797CF51901701926e',
//   gasUsed: { BigNumber: "51558" },
//   logs: [
//     {
//       address: '0x70ff5c5B1Ad0533eAA5489e0D5Ea01485d530674',
//       blockHash: '0x9340a9c7efafbb4ad9a6ebda62e3311ec5fc40ddcc733632938a988c8c62b885',
//       blockNumber: 60330,
//       data: '0x0000000000000000000000000000000000000000000000001111d67bb1bb0000',
//       logIndex: 0,
//       topics: [
//         '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
//         '0x00000000000000000000000046e0726ef145d92dea66d38797cf51901701926e',
//         '0x0000000000000000000000005555763613a12d8f3e73be831dff8598089d3dca'
//       ],
//       transactionHash: '0xafdc1f7ec14e2e05a39826c134c9157e0011fc784aef623df3fdf9b9d29f3ac8',
//       transactionIndex: 0
//     }
//   ],
//   logsBloom: '0x00000000000000000800000000000004000000000000000000000000000040000000000000000000000000000000000000000000000000000000020000000000000000000000000000000008000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010002000000000000000000000000000000000000000000000000000000000000000000000001000000000200000000000000000000000000000000000000',
//   status: 1,
//   to: '0x70ff5c5B1Ad0533eAA5489e0D5Ea01485d530674',
//   transactionHash: '0xafdc1f7ec14e2e05a39826c134c9157e0011fc784aef623df3fdf9b9d29f3ac8',
//   transactionIndex: 0,
//   type: 2
// }

And to work-around failing transaction tests, you could do something like that to prevent an error in your test file:

    it("Adds an institute with valid details", async function () {
      // ...
      if (logs.length < 1) {
        assert.equal(
            false,
            true,
            'No events emitted, receipt log`s length is < 1'
        );
      }

      assert.equal(logs.length, 1);
      assert.equal(logs[0].event, "instituteAdded");
      assert.equal(
        receipt.logs[0].args._institute_name,
        mockInstitute.institute_name
      );
    });

(For reference: https://docs.ethers.org/v5/api/contract/example/)

1
  • 1
    The above one works but in test file I have to do javascript assert.equal(logs.length, 1); assert.equal(logs[0].fragment.name, "instituteAdded"); assert.equal(logs[0].args._instituteName, mockInstitute.institute_name);
    – Nikhil Raj
    Feb 18 at 8:43

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.