체인의정석

hardhat 테스트 코드에서 beforeEach 사용하기 VS fixtures 사용하기 본문

블록체인/Ethers & web3

hardhat 테스트 코드에서 beforeEach 사용하기 VS fixtures 사용하기

체인의정석 2022. 7. 19. 17:53
728x90
반응형

하드햇에서 첫번째로 작성하게 되는 테스트 코드는 해피케이스에 대한 시나리오 테스트이다.

이 테스트가 끝나게 되면 테스트 커버리지를 100% 까지 올리기 위해서 온갖 오류를 직접 발생시켜야 한다.

 

하지만 스마트컨트렉트에서 대부분의 함수들은 특정 조건에서만 실행이 가능하기 때문에 특정 시점에서 분기가 일어나서 오류가 테스트되는 경우 앞에 기본 세팅은 반복이 되어야 한다는 문제점이 있다.

 

여기에 따라 beforeEach를 사용하기로 하였다.

 

일단 기본 포맷은 아래와 같다.

https://hardhat.org/hardhat-runner/docs/guides/test-contracts#using-fixtures

 

Hardhat | Ethereum development environment for professionals by Nomic Foundation

Hardhat is an Ethereum development environment. Compile your contracts and run them on a development network. Get Solidity stack traces, console.log and more.

hardhat.org

import { Contract } from "ethers";

describe("Lock", function () {
  let lock: Contract;
  let unlockTime: number;
  let lockedAmount = 1_000_000_000;

  beforeEach(async function () {
    const ONE_YEAR_IN_SECS = 365 * 24 * 60 * 60;
    unlockTime = (await helpers.time.latest()) + ONE_YEAR_IN_SECS;

    const Lock = await ethers.getContractFactory("Lock");
    lock = await Lock.deploy(unlockTime, { value: lockedAmount });
  });

  it("some test", async function () {
    // use the deployed contract
  });
});

보면 describe 안에 변수를 정의한 후 beforeEach에 컨트렉트 배포를 넣은 후 동일 레벨 상에서 it을 넣어주는 식으로 코드를 작성하였다.

이러한 경우 초기에 작성한 케이스는 이미 입증이 되었기 때문에 해당 테스트 코드를 그대로 beforeEach안에 두고 테스트 케이스를 작성하는 방법이 가능하다. 이미 검증된 상태부터 이어서 예외상황에 대한 테스트를 하는 것이다. 그리고 콘솔로그는 찍히기 때문에 로그 값으로 중간중간 디버깅을 하거나 값을 체크할 수도 있다.

 

하지만 복잡하고 중간중간 커스터마이징이 필요한 경우라면 아래와 같이도 사용이 가능하다.

describe("Lock", function () {
  async function deployOneYearLockFixture() {
    const lockedAmount = 1_000_000_000;
    const ONE_YEAR_IN_SECS = 365 * 24 * 60 * 60;
    const unlockTime = (await time.latest()) + ONE_YEAR_IN_SECS;

    const Lock = await ethers.getContractFactory("Lock");
    const lock = await Lock.deploy(unlockTime, { value: lockedAmount });

    return { lock, unlockTime, lockedAmount };
  }

  it("Should set the right unlockTime", async function () {
    const { lock, unlockTime } = await loadFixture(deployOneYearLockFixture);

    // assert that the value is correct
    expect(await lock.unlockTime()).to.equal(unlockTime);
  });

  it("Should revert with the right error if called too soon", async function () {
    const { lock } = await loadFixture(deployOneYearLockFixture);

    await expect(lock.withdraw()).to.be.revertedWith("You can't withdraw yet");
  });
});

여기서 사용된 것이 loadFixture 이다. 

loadFixture를 사용하면 하드햇 네트워크가 fixture 라는 것을 받아서 원하는 상태로 만들어 준다. 처음 loadFixture가 사용될 때는 LoadFixture가 실행되지만 그 다음부터는 실행 이후의 시점으로 네트워크가 리셋된다.

 

이에 따라서 코드 자체도 깔끔해지고 테스트 코드의 부하도 줄어들게 된다.

근데 현재 구조에서는 워낙 

load fixture의 경우 다만 함수를 여러개 만들어서 관리하게 되면 조금 더 정신 없을 수도 있다는 생각이 들었다.

 

결국 테스트 코드가 복잡해지면 두개를 모두 사용하여 적절히 해야 더 가독성이 좋은 테스트 코드가 나오는거 같다.

이 부분은 테스트 코드 작성이 마무리될 때 쯔음 업데이트 해보겠다.

 

 

 

728x90
반응형
Comments