> For the complete documentation index, see [llms.txt](https://whs3-doge5k.gitbook.io/smartcontract-security-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://whs3-doge5k.gitbook.io/smartcontract-security-docs/basics/6.-foundry/forge/forge-test.md).

# forge test

## 스마트 컨트랙트 테스트

### 테스트의 필요성

스마트 컨트랙트를 테스트하는 이유는 이더리움의 상태를 변경하는 작업(배포, 호출 등)에 이더(가스 비용)가 소모되기 때문이다. 실수나 오류로 인해 잘못된 방식으로 상태를 변경하면 이더만 소모되고 원하는 결과를 얻지 못할 수 있다. 이를 방지하기 위해 Foundry에서는 forge test 명령어로 안전하게 테스트할 수 있는 환경을 제공한다.

***

### 기본 테스트 명령어

```bash
$ forge test
```

* /test와 /src 폴더 내 Solidity 파일에서 test로 시작하는 메서드를 자동으로 실행한다.
* 예시: test\_Increment(), testFuzz\_SetNumber() 등

#### 테스트 예시 파일

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

//Test용 라이브러리 사용
import {Test, console} from "forge-std/Test.sol";
//기본으로 주어지는 Counter.sol 파일 import (테스트 해야하는 solidity 파일이다.)
import {Counter} from "src/Counter.sol";

//Test를 상속받은 CounterTest
contract CounterTest is Test {
    Counter public counter;
    
//테스트용 메소드 setUp (매 test마다 동작하여 상태를 초기화함) 
    function setUp() public {
        counter = new Counter();
        counter.setNumber(0);
    }
//테스트시 동작하는 메소드
    function test_Increment() public {
        counter.increment(); //Counter에 정의된 메소드를 실행한다.
        assertEq(counter.number(), 1);
    }
//테스트시 동작하는 메소드
    function testFuzz_SetNumber(uint256 x) public {
        counter.setNumber(x); //Counter에 정의된 메소드를 실행한다.
        assertEq(counter.number(), x);
    }
}
```

***

### 치트코드 (Cheatcodes)

테스트 환경에서 EVM의 제약을 무시하고 상태를 조작할 수 있는 강력한 도구.

| 치트코드                                 | 설명                         |
| ------------------------------------ | -------------------------- |
| vm.warp(uint256)                     | 블록 타임스탬프 변경                |
| vm.roll(uint256)                     | 블록 넘버 변경                   |
| vm.prank(address)                    | 다음 트랜잭션의 `msg.sender` 설정   |
| vm.startPrank(addr) / vm.stopPrank() | 여러 트랜잭션 동안 `msg.sender` 고정 |
| vm.expectRevert()                    | 특정 코드가 revert 되길 기대        |
| vm.expectEmit()                      | 특정 이벤트 발생 기대               |
| vm.deal(address, uint256)            | 특정 주소에 Ether 할당            |
| vm.store(address, key)               | 컨트랙트의 storage 직접 수정        |
| vm.load(address, key)                | 컨트랙트의 storage 직접 조회        |
| vm.mockCall(...)                     | 외부 컨트랙트 호출 결과 모킹           |
| vm.fee(uint256)                      | gas price 설정               |
| vm.label(address, string)            | 주소에 라벨 지정 (디버깅용)           |

#### 예시

```solidity
pragma solidity ^0.8.10;
import {stdError} from "forge-std/StdError.sol";
import {Test} from "forge-std/Test.sol";

contract ContractBTest is Test {
    uint256 testNumber;

    function setUp() public {
        testNumber = 42; //testNumber를 42로 초기화
    }

    function test_NumberIs42() public {
        assertEq(testNumber, 42);  //testNumber가 42랑 같은지 확인하는 메소드
    }

 
    function testRevert_Subtract43() public {
        vm.expectRevert(stdError.arithmeticError); //아래 줄에서 Revert가 발생하지 않으면 오류가 나는 메소드
        testNumber -= 43; //여기서 오버플로우 발생듸므로 Revert!!
    }
}
```

<figure><img src="https://2462457462-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLf9vwRuaws92ULgGvzTi%2Fuploads%2FIaTz3GG6fORCmS96NeMR%2Fimage%20(2).png?alt=media&amp;token=e4e69f21-0e3d-4adb-ace0-3f50cf77751b" alt=""><figcaption></figcaption></figure>

해당 코드에서는 오버플로우가 발생하지 않으면 이와같이 revert를 발생시킨다.

### 테스트 결과 출력 (Trace)

테스트의 로그를 띄울 수 있다. 로그의 내용은 v의 개수로 결정된다.

| 옵션       | 출력 내용                              |
| -------- | ---------------------------------- |
| `-v`     | 테스트 통계 표시                          |
| `-vv`    | emit된 로그 출력                        |
| `-vvv`   | 실패 테스트의 스택 트레이스 표시                 |
| `-vvvv`  | 모든 테스트의 스택 트레이스 + 실패 테스트의 setUp 표시 |
| `-vvvvv` | 전체 스택 + setup 트레이스 모두 표시           |

<figure><img src="https://2462457462-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLf9vwRuaws92ULgGvzTi%2Fuploads%2FSx80VqP2BvC2Qx1PcPqS%2Fimage%20(3).png?alt=media&amp;token=19b8b608-582f-4106-8823-5ac76a1a99a3" alt=""><figcaption></figcaption></figure>

***

### 가스 리포트

#### 실행 명령어

```bash
forge test --gas-report
```

* 테스트별로 가스 소비량을 출력한다.

<figure><img src="https://2462457462-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLf9vwRuaws92ULgGvzTi%2Fuploads%2FiCn8kousMUjaYRQLta23%2Fimage%20(4).png?alt=media&amp;token=489378c5-88f9-4fed-a527-2c07b1f4ed4d" alt=""><figcaption></figcaption></figure>

스냅샷 저장

```bash
forge snapshot
```

* 각 테스트 함수의 가스 사용량을 snapshot 파일로 저장한다.

<figure><img src="https://2462457462-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLf9vwRuaws92ULgGvzTi%2Fuploads%2FgAhcsnWAyhMbFaasjx7P%2Fimage%20(5).png?alt=media&amp;token=4c6f51ac-f354-4543-9e5c-aa87ab015874" alt=""><figcaption></figcaption></figure>

***

### 포크 테스트 (Fork Testing)

실제 블록체인 상태를 복제하여 로컬 테스트를 수행할 수 있다.

```bash
forge test --fork-url <RPC_URL>
```

* 실제 체인의 컨트랙트, 계정 상태, 저장소 데이터를 로컬로 복제

특정 블록 기준으로도 복제 가능:

```bash
forge test --fork-url <RPC_URL> --fork-block-number <BLOCK_NUM>
```

> forge의 fork는 일회성 테스트 용도이며 anvil은 지속적으로 네트워크 상태를 유지함

***

### 실패 테스트 재실행

```bash
forge test --rerun
```

* 가장 최근에 실패한 테스트만 선택적으로 재실행 가능
* 실패 테스트는 \~/.foundry/cache/test-failure에 저장됨

***

### 고급 테스트 (Advanced Testing)

#### 퍼즈 테스트 (Fuzz Testing)

* 다양한 랜덤 입력값을 자동으로 넣어 테스트 수행
* 엣지 케이스나 취약점을 발견하는 데 유용

&#x20;불변성 테스트 (Invariant Testing)

* 어떤 상태가 항상 유지되어야 함을 검증
* 예: `balance >= 0` 항상 성립해야 함

#### &#x20;차등 테스트 (Differential Testing)

* 동일한 입력값을 서로 다른 구현체에 넣고 결과 비교
* 구현 간 일관성 검증에 효과적
