10X ethereum hardhat : Тестирование контрактов

— введение

— Написание тестов

— Использование другого аккаунта

— Повторное использование общих настроек тестов с помощью фикстур

— Полный охват

— Учебники по hardhat , hardhat 教程

— Контакт 联系方式

— введение

Написание автоматизированных тестов при создании смарт-контрактов имеет огромное значение, поскольку на кону стоят деньги пользователей.

Для тестирования нашего контракта мы будем использовать Hardhat Network, локальную сеть Ethereum, предназначенную для разработки. Она встроена в Hardhat и используется в качестве сети по умолчанию. Вам не нужно ничего настраивать, чтобы использовать ее.

В наших тестах мы будем использовать ethers.js для взаимодействия с контрактом Ethereum, который мы создали в предыдущем разделе, а в качестве программы запуска тестов мы будем использовать Mocha.

— Написание тестов

Создайте новый каталог test в корневом каталоге нашего проекта и создайте в нем новый файл Token.js.

Начнем с приведенного ниже кода. Мы объясним его далее, а пока вставьте его в Token.js:

const { expect } = require("chai");

describe("Token contract", function () {
  it("Deployment should assign the total supply of tokens to the owner", async function () {
    const [owner] = await ethers.getSigners();

    const Token = await ethers.getContractFactory("Token");

    const hardhatToken = await Token.deploy();

    const ownerBalance = await hardhatToken.balanceOf(owner.address);
    expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);
  });
});
Войти в полноэкранный режим Выйти из полноэкранного режима

В терминале запустите npx hardhat test. Вы должны увидеть следующий результат:

$ npx hardhat test

  Token contract
    ✓ Deployment should assign the total supply of tokens to the owner (654ms)


  1 passing (663ms)
Вход в полноэкранный режим Выход из полноэкранного режима

Это означает, что тест пройден. Давайте теперь объясним каждую строку:

const [owner] = await ethers.getSigners();
Войти в полноэкранный режим Выход из полноэкранного режима

Signer в ethers.js — это объект, представляющий счет Ethereum. Он используется для отправки транзакций контрактам и другим счетам. Здесь мы получаем список счетов в узле, к которому мы подключены, в данном случае это Hardhat Network, и сохраняем только первый.

Переменная ethers доступна в глобальной области видимости. Если вы любите, чтобы ваш код всегда был явным, вы можете добавить эту строку сверху:

const { ethers } = require("hardhat");
Войти в полноэкранный режим Выйти из полноэкранного режима
const Token = await ethers.getContractFactory("Token");
Войти в полноэкранный режим Выйти из полноэкранного режима

Фабрика ContractFactory в ethers.js — это абстракция, используемая для развертывания новых смарт-контрактов, поэтому Token здесь — это фабрика для экземпляров нашего контракта с токенами.

const hardhatToken = await Token.deploy();
Вход в полноэкранный режим Выход из полноэкранного режима

Вызов deploy() на ContractFactory начнет развертывание и вернет Promise, который разрешается в Contract. Это объект, который имеет метод для каждой функции вашего смарт-контракта.

const ownerBalance = await hardhatToken.balanceOf(owner.address);
Вход в полноэкранный режим Выход из полноэкранного режима

Как только контракт развернут, мы можем вызвать методы нашего контракта на hardhatToken. Здесь мы получаем баланс счета владельца, вызывая метод balanceOf() контракта.

Напомним, что счет, который развертывает токен, получает весь его запас. По умолчанию экземпляры ContractFactory и Contract связаны с первым подписавшим. Это означает, что учетная запись в переменной owner выполнила развертывание, и balanceOf() должна вернуть всю сумму запаса.

expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);
Вход в полноэкранный режим Выход из полноэкранного режима

Здесь мы снова используем наш экземпляр Contract для вызова функции смарт-контракта в нашем коде Solidity. totalSupply() возвращает сумму поставки токена, и мы проверяем, что она равна ownerBalance, как и должно быть.

Для этого мы используем Chai, которая является популярной библиотекой утверждений JavaScript. Эти функции утверждения называются «матчеры», и те, которые мы используем здесь, взяты из плагина @nomicfoundation/hardhat-chai-matchers, который расширяет Chai многими матчерами, полезными для тестирования смарт-контрактов.

— Использование другой учетной записи

Если вам нужно протестировать свой код, отправив транзакцию со счета (или Signer в терминологии ethers.js), отличного от счета по умолчанию, вы можете использовать метод connect() на объекте ethers.js Contract, чтобы подключить его к другому счету, как показано ниже:

const { expect } = require("chai");

describe("Token contract", function () {
  // ...previous test...

  it("Should transfer tokens between accounts", async function() {
    const [owner, addr1, addr2] = await ethers.getSigners();

    const Token = await ethers.getContractFactory("Token");

    const hardhatToken = await Token.deploy();

    // Transfer 50 tokens from owner to addr1
    await hardhatToken.transfer(addr1.address, 50);
    expect(await hardhatToken.balanceOf(addr1.address)).to.equal(50);

    // Transfer 50 tokens from addr1 to addr2
    await hardhatToken.connect(addr1).transfer(addr2.address, 50);
    expect(await hardhatToken.balanceOf(addr2.address)).to.equal(50);
  });
});
Вход в полноэкранный режим Выход из полноэкранного режима

— Повторное использование общих настроек тестов с помощью фикстур

Два теста, которые мы написали, начинаются с их настройки, что в данном случае означает развертывание контракта токена. В более сложных проектах эта настройка может включать в себя несколько развертываний и других транзакций. Выполнение этого в каждом тесте означает дублирование кода. Кроме того, выполнение множества транзакций в начале каждого теста может сделать набор тестов намного медленнее.

Вы можете избежать дублирования кода и повысить производительность вашего набора тестов, используя фикстуры. Фикстура — это функция настройки, которая выполняется только при первом вызове. При последующих вызовах, вместо повторного запуска, Hardhat будет возвращать состояние сети к тому, которое было в момент после первоначального выполнения приспособления.

const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");
const { expect } = require("chai");

describe("Token contract", function () {
  async function deployTokenFixture() {
    const Token = await ethers.getContractFactory("Token");
    const [owner, addr1, addr2] = await ethers.getSigners();

    const hardhatToken = await Token.deploy();

    await hardhatToken.deployed();

    // Fixtures can return anything you consider useful for your tests
    return { Token, hardhatToken, owner, addr1, addr2 };
  }

  it("Should assign the total supply of tokens to the owner", async function () {
    const { hardhatToken, owner } = await loadFixture(deployTokenFixture);

    const ownerBalance = await hardhatToken.balanceOf(owner.address);
    expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);
  });

  it("Should transfer tokens between accounts", async function () {
    const { hardhatToken, owner, addr1, addr2 } = await loadFixture(
      deployTokenFixture
    );

    // Transfer 50 tokens from owner to addr1
    await expect(
      hardhatToken.transfer(addr1.address, 50)
    ).to.changeTokenBalances(hardhatToken, [owner, addr1], [-50, 50]);

    // Transfer 50 tokens from addr1 to addr2
    // We use .connect(signer) to send a transaction from another account
    await expect(
      hardhatToken.connect(addr1).transfer(addr2.address, 50)
    ).to.changeTokenBalances(hardhatToken, [addr1, addr2], [-50, 50]);
  });
});
Вход в полноэкранный режим Выход из полноэкранного режима

Здесь мы написали функцию deployTokenFixture, которая выполняет необходимую настройку и возвращает все значения, которые мы используем позже в тестах. Затем в каждом тесте мы используем loadFixture для запуска фикстуры и получения этих значений. loadFixture выполнит настройку в первый раз и быстро вернется к этому состоянию в других тестах.

— Полное покрытие

Теперь, когда мы рассмотрели основы, которые понадобятся вам для тестирования ваших контрактов, вот полный набор тестов для токена с большим количеством дополнительной информации о Mocha и о том, как структурировать ваши тесты. Мы рекомендуем внимательно прочитать его.

// This is an example test file. Hardhat will run every *.js file in `test/`,
// so feel free to add new ones.

// Hardhat tests are normally written with Mocha and Chai.

// We import Chai to use its asserting functions here.
const { expect } = require("chai");

// We use `loadFixture` to share common setups (or fixtures) between tests.
// Using this simplifies your tests and makes them run faster, by taking
// advantage of Hardhat Network's snapshot functionality.
const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");

// `describe` is a Mocha function that allows you to organize your tests.
// Having your tests organized makes debugging them easier. All Mocha
// functions are available in the global scope.
//
// `describe` receives the name of a section of your test suite, and a
// callback. The callback must define the tests of that section. This callback
// can't be an async function.
describe("Token contract", function () {
  // We define a fixture to reuse the same setup in every test. We use
  // loadFixture to run this setup once, snapshot that state, and reset Hardhat
  // Network to that snapshopt in every test.
  async function deployTokenFixture() {
    // Get the ContractFactory and Signers here.
    const Token = await ethers.getContractFactory("Token");
    const [owner, addr1, addr2] = await ethers.getSigners();

    // To deploy our contract, we just have to call Token.deploy() and await
    // its deployed() method, which happens onces its transaction has been
    // mined.
    const hardhatToken = await Token.deploy();

    await hardhatToken.deployed();

    // Fixtures can return anything you consider useful for your tests
    return { Token, hardhatToken, owner, addr1, addr2 };
  }

  // You can nest describe calls to create subsections.
  describe("Deployment", function () {
    // `it` is another Mocha function. This is the one you use to define each
    // of your tests. It receives the test name, and a callback function.
    //
    // If the callback function is async, Mocha will `await` it.
    it("Should set the right owner", async function () {
      // We use loadFixture to setup our environment, and then assert that
      // things went well
      const { hardhatToken, owner } = await loadFixture(deployTokenFixture);

      // `expect` receives a value and wraps it in an assertion object. These
      // objects have a lot of utility methods to assert values.

      // This test expects the owner variable stored in the contract to be
      // equal to our Signer's owner.
      expect(await hardhatToken.owner()).to.equal(owner.address);
    });

    it("Should assign the total supply of tokens to the owner", async function () {
      const { hardhatToken, owner } = await loadFixture(deployTokenFixture);
      const ownerBalance = await hardhatToken.balanceOf(owner.address);
      expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);
    });
  });

  describe("Transactions", function () {
    it("Should transfer tokens between accounts", async function () {
      const { hardhatToken, owner, addr1, addr2 } = await loadFixture(
        deployTokenFixture
      );
      // Transfer 50 tokens from owner to addr1
      await expect(
        hardhatToken.transfer(addr1.address, 50)
      ).to.changeTokenBalances(hardhatToken, [owner, addr1], [-50, 50]);

      // Transfer 50 tokens from addr1 to addr2
      // We use .connect(signer) to send a transaction from another account
      await expect(
        hardhatToken.connect(addr1).transfer(addr2.address, 50)
      ).to.changeTokenBalances(hardhatToken, [addr1, addr2], [-50, 50]);
    });

    it("should emit Transfer events", async function () {
      const { hardhatToken, owner, addr1, addr2 } = await loadFixture(
        deployTokenFixture
      );

      // Transfer 50 tokens from owner to addr1
      await expect(hardhatToken.transfer(addr1.address, 50))
        .to.emit(hardhatToken, "Transfer")
        .withArgs(owner.address, addr1.address, 50);

      // Transfer 50 tokens from addr1 to addr2
      // We use .connect(signer) to send a transaction from another account
      await expect(hardhatToken.connect(addr1).transfer(addr2.address, 50))
        .to.emit(hardhatToken, "Transfer")
        .withArgs(addr1.address, addr2.address, 50);
    });

    it("Should fail if sender doesn't have enough tokens", async function () {
      const { hardhatToken, owner, addr1 } = await loadFixture(
        deployTokenFixture
      );
      const initialOwnerBalance = await hardhatToken.balanceOf(owner.address);

      // Try to send 1 token from addr1 (0 tokens) to owner (1000 tokens).
      // `require` will evaluate false and revert the transaction.
      await expect(
        hardhatToken.connect(addr1).transfer(owner.address, 1)
      ).to.be.revertedWith("Not enough tokens");

      // Owner balance shouldn't have changed.
      expect(await hardhatToken.balanceOf(owner.address)).to.equal(
        initialOwnerBalance
      );
    });
  });
});
Вход в полноэкранный режим Выход из полноэкранного режима

Вот как должен выглядеть вывод npx hardhat test на фоне полного набора тестов:

$ npx hardhat test

  Token contract
    Deployment
      ✓ Should set the right owner
      ✓ Should assign the total supply of tokens to the owner
    Transactions
      ✓ Should transfer tokens between accounts (199ms)
      ✓ Should fail if sender doesn’t have enough tokens
      ✓ Should update balances after transfers (111ms)


  5 passing (1s)
Вход в полноэкранный режим Выход из полноэкранного режима

Помните, что когда вы запускаете npx hardhat test, ваши контракты будут автоматически скомпилированы, если они изменились с момента последнего запуска тестов.

— hardhat Tutorials , hardhat 教程

CN 中文 Github hardhat 教程 : github.com/565ee/hardhat_CN

CN 中文 CSDN hardhat 教程 : blog.csdn.net/wx468116118

EN 英文 Github hardhat Tutorials : github.com/565ee/hardhat_EN

— Контакт 联系方式

Домашняя страница : 565.ee

GitHub : github.com/565ee

Электронная почта : 565.eee@gmail.com

Facebook : facebook.com/565.ee

Twitter : twitter.com/565_eee

Telegram : t.me/ee_565

Оцените статью
devanswers.ru
Добавить комментарий