Solidity - Array.length 是继承合约的 0

Solidity - Array.length is 0 from inherited contract

我正在使用 Solidity/Truffle。我有两个合同,名为 Category 和 Post。 Post 继承自类别 (Post is Category )。 合同类别有一个名为 categories 的数组。我在合同类别中有一个名为 isCategoryExists 的函数。 当我尝试从合同类别调用此函数时,一切正常。但是当我想从合同 Post 调用这个函数时,我收到 false 因为 categories.length0.

Category.sol:

ragma solidity >=0.4.22 <0.9.0;
import "./ICategory.sol";

/// @title Create, edit and manage categories
contract Category is ICategory {
  // State variables
  uint256 currentIndex;
  CategoryStruct[] private categories;
  mapping(address => CategoryStruct[]) categoriesByUser;


  // Modifiers
  modifier onlyValidInput(CategoryInputStruct memory _input) {
    bytes memory errCode;
    bytes memory title = bytes(_input.title);

    if (title.length == 0) {
      errCode = bytes("invalidTitle");
    }

    if (errCode.length > 0) {
      revert(string(errCode));
    }
    _;
  }

  modifier onlyValidIndex(uint256 index) {
    if (isCategoryExists(index)) {
      _;
    } else {
      revert("Invalid index.");
    }
  }

  // Constructor

  constructor() {
    currentIndex = categories.length;
  }

  // Functions

  /// @notice Check if category exists
  function isCategoryExists(uint256 index) public view returns (bool) {
    if (index >= categories.length) {
      return false;
    }

    return categories[index].isExist;
  }

  /// @notice This function creates a category.
  /// @dev Before this function, the entered data is validated with onlyValidInput modifier.
  function createCategory(
    CategoryInputStruct memory _input,
    LocationStruct memory _location
  ) external onlyValidInput(_input) returns (bool) {
    CategoryStruct memory newCategory = CategoryStruct({
      id: currentIndex,
      user: msg.sender,
      title: _input.title,
      isExist: true
    });

    categories.push(newCategory);
    categoriesByUser[msg.sender].push(newCategory);
    emit CategoryCreated(currentIndex);
    currentIndex++;
    return true;
  }
}

Post.sol:

pragma solidity >=0.4.22 <0.9.0;
import "../category/Category.sol";

/// @title Create, edit and manage posts
contract Post is Category {
  // State variables
  uint256 currentPostIndex;

  struct PostStruct {
    uint256 id;
    address user;
    string title;
    string body;
    uint256 categoryId;
  }
  struct PostInputStruct {
    string title;
    string body;
    uint256 categoryId;
  }

  PostStruct[] private posts;
  mapping(address => uint256[]) postIndexesByUser; // example: 0x01234 => [3, 5, 24, 112, 448]

  // Modifiers
  modifier onlyValidPostInput(PostInputStruct memory _input) {
    bytes memory errCode;
    bytes memory title = bytes(_input.title);
    bytes memory body = bytes(_input.body);
    uint256 categoryId = uint256(_input.categoryId);

    if (title.length == 0) {
      errCode = bytes("invalidTitle");
    } else if (body.length == 0) {
      errCode = bytes("invalidBody");
    }
    if (errCode.length > 0) {
      revert(string(errCode));
    }
    _;
  }

  // Constructor

  constructor() {
    currentPostIndex = posts.length;
  }

  // Functions

  /// @notice This function creates a post.
  /// @dev Before this function, the entered data is validated with onlyValidPostInput modifier.
  function createPost(PostInputStruct memory _input)
    external
    onlyValidPostInput(_input)
    returns (bool)
  {
    bool isExist = isCategoryExists(_input.categoryId);

    PostStruct memory newPost = PostStruct({
      id: currentPostIndex,
      user: msg.sender,
      title: _input.title,
      body: _input.body,
      categoryId: _input.categoryId
    });

    posts.push(newPost);
    postIndexesByUser[msg.sender].push(currentPostIndex);

    currentPostIndex++;
    return true;
  }
}

我在 Truffle 中的 JS 测试环境中进行了此调用。

我猜的: 我认为每次 运行 测试时一切都会重置。我不肯定。这有点奇怪,浪费了我几天的时间。

Category.test.js:

const Chance = require("chance");
const Category = artifacts.require("Category");

const chance = new Chance();

contract("Category", (accounts) => {
  // Setup 1 account.
  const accountOne = accounts[0];
  const accountTwo = accounts[1];
  let categoryInstance;

  before(async () => {
    categoryInstance = await Category.deployed();
  });

  it("should create a category", async () => {
    const title = chance.sentence();

    // Create category
    const categoryCreated = await categoryInstance.createCategory([title], {
      from: accountOne,
    });

    // Get a category by array index
    const category = await categoryInstance.getCategoryByIndex(0);

    assert.equal(category.id, 0, "There is no data for this index");
    assert.equal(category.title, title, "title is not equal");
  });

  it("Should return true because a category exists", async () => {
    const isExists = await categoryInstance.isCategoryExists(0, {
      from: accountOne,
    });

    assert.equal(isExists, true, "Category exists but result is false.");
  });
});

Post.test.js

const Chance = require("chance");
const Category = artifacts.require("Category");
const Post = artifacts.require("post");

const chance = new Chance();

contract("Post", (accounts) => {
  // Setup 1 account.
  const accountOne = accounts[0];
  const accountTwo = accounts[1];

  let categoryInstance;
  let postInstance;

  before(async () => {
    // Create a sample category
    categoryInstance = await Category.deployed();
    const title = chance.sentence();

    // Create category
    const categoryCreated = await categoryInstance.createCategory(
      [title, purpose, area],
      [polygon],
      {
        from: accountOne,
      }
    );

    postInstance = await Post.deployed();
  });

  it("should create a post", async () => {
    // Generate sample data
    const title = chance.sentence();
    const body = chance.paragraph();
    const thumbnail = chance.url();
    const categoryId = 0;

    // Create post
    const postCreated = await postInstance.createPost(
      [title, body, thumbnail, categoryId],
      {
        from: accountOne,
      }
    );

    // Get a post by array index
    const post = await postInstance.getPostByIndex(0);

    assert.equal(post.id, 0, "There is no data for this index");
    assert.equal(post.title, title, "title is not equal");
    assert.equal(post.categoryId, categoryId, "categoryId is not equal");
  });
});

问题是你误解了继承的概念。 Post 继承自 Category 的事实并不意味着 Post 的实例与 Category 的实例共享状态。这意味着 Post 也是一个 Category,因此 Post 的 object/instance 具有相同的状态(变量)和行为(functions/methods) Category 包含在自身中。在你的情况下,这实际上意味着 Post 的对象有自己的数组 categories 并且当你调用 createPost 时正在检查那个数组,它当然是空的,因为你从来没有使用该对象添加了一个类别。数组可以是 non-empty 的唯一方法是从 postInstance 而不是从 categoryInstance.

调用 createCategory

P.S。 只是为了快速澄清我所说的“不应保留任何内容”的意思。测试套件应该以每个测试都是独立的方式创建,任何测试都不应该依赖于其他测试的执行。人们应该能够 运行 自己进行套件中的任何测试并通过测试。我最初认为这是问题所在,因为您没有共享整个代码。