{"id":"web3-testing","name":"web3-testing","summary":"HardhatとFoundryを用いて、ユニットテスト、統合テスト、メインネットフォークを用いてスマートコントラクトを包括的にテストします。","body":"# Web3 Smart Contract Testing\n\nMaster comprehensive testing strategies for smart contracts using Hardhat, Foundry, and advanced testing patterns.\n\n## When to Use This Skill\n\n- Writing unit tests for smart contracts\n- Setting up integration test suites\n- Performing gas optimization testing\n- Fuzzing for edge cases\n- Forking mainnet for realistic testing\n- Automating test coverage reporting\n- Verifying contracts on Etherscan\n\n## Hardhat Testing Setup\n\n```javascript\n// hardhat.config.js\nrequire(\"@nomicfoundation/hardhat-toolbox\");\nrequire(\"@nomiclabs/hardhat-etherscan\");\nrequire(\"hardhat-gas-reporter\");\nrequire(\"solidity-coverage\");\n\nmodule.exports = {\n  solidity: {\n    version: \"0.8.19\",\n    settings: {\n      optimizer: {\n        enabled: true,\n        runs: 200,\n      },\n    },\n  },\n  networks: {\n    hardhat: {\n      forking: {\n        url: process.env.MAINNET_RPC_URL,\n        blockNumber: 15000000,\n      },\n    },\n    goerli: {\n      url: process.env.GOERLI_RPC_URL,\n      accounts: [process.env.PRIVATE_KEY],\n    },\n  },\n  gasReporter: {\n    enabled: true,\n    currency: \"USD\",\n    coinmarketcap: process.env.COINMARKETCAP_API_KEY,\n  },\n  etherscan: {\n    apiKey: process.env.ETHERSCAN_API_KEY,\n  },\n};\n```\n\n## Unit Testing Patterns\n\n```javascript\nconst { expect } = require(\"chai\");\nconst { ethers } = require(\"hardhat\");\nconst {\n  loadFixture,\n  time,\n} = require(\"@nomicfoundation/hardhat-network-helpers\");\n\ndescribe(\"Token Contract\", function () {\n  // Fixture for test setup\n  async function deployTokenFixture() {\n    const [owner, addr1, addr2] = await ethers.getSigners();\n\n    const Token = await ethers.getContractFactory(\"Token\");\n    const token = await Token.deploy();\n\n    return { token, owner, addr1, addr2 };\n  }\n\n  describe(\"Deployment\", function () {\n    it(\"Should set the right owner\", async function () {\n      const { token, owner } = await loadFixture(deployTokenFixture);\n      expect(await token.owner()).to.equal(owner.address);\n    });\n\n    it(\"Should assign total supply to owner\", async function () {\n      const { token, owner } = await loadFixture(deployTokenFixture);\n      const ownerBalance = await token.balanceOf(owner.address);\n      expect(await token.totalSupply()).to.equal(ownerBalance);\n    });\n  });\n\n  describe(\"Transactions\", function () {\n    it(\"Should transfer tokens between accounts\", async function () {\n      const { token, owner, addr1 } = await loadFixture(deployTokenFixture);\n\n      await expect(token.transfer(addr1.address, 50)).to.changeTokenBalances(\n        token,\n        [owner, addr1],\n        [-50, 50],\n      );\n    });\n\n    it(\"Should fail if sender doesn't have enough tokens\", async function () {\n      const { token, addr1 } = await loadFixture(deployTokenFixture);\n      const initialBalance = await token.balanceOf(addr1.address);\n\n      await expect(\n        token.connect(addr1).transfer(owner.address, 1),\n      ).to.be.revertedWith(\"Insufficient balance\");\n    });\n\n    it(\"Should emit Transfer event\", async function () {\n      const { token, owner, addr1 } = await loadFixture(deployTokenFixture);\n\n      await expect(token.transfer(addr1.address, 50))\n        .to.emit(token, \"Transfer\")\n        .withArgs(owner.address, addr1.address, 50);\n    });\n  });\n\n  describe(\"Time-based tests\", function () {\n    it(\"Should handle time-locked operations\", async function () {\n      const { token } = await loadFixture(deployTokenFixture);\n\n      // Increase time by 1 day\n      await time.increase(86400);\n\n      // Test time-dependent functionality\n    });\n  });\n\n  describe(\"Gas optimization\", function () {\n    it(\"Should use gas efficiently\", async function () {\n      const { token } = await loadFixture(deployTokenFixture);\n\n      const tx = await token.transfer(addr1.address, 100);\n      const receipt = await tx.wait();\n\n      expect(receipt.gasUsed).to.be.lessThan(50000);\n    });\n  });\n});\n```\n\n## Foundry Testing (Forge)\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"forge-std/Test.sol\";\nimport \"../src/Token.sol\";\n\ncontract TokenTest is Test {\n    Token token;\n    address owner = address(1);\n    address user1 = address(2);\n    address user2 = address(3);\n\n    function setUp() public {\n        vm.prank(owner);\n        token = new Token();\n    }\n\n    function testInitialSupply() public {\n        assertEq(token.totalSupply(), 1000000 * 10**18);\n    }\n\n    function testTransfer() public {\n        vm.prank(owner);\n        token.transfer(user1, 100);\n\n        assertEq(token.balanceOf(user1), 100);\n        assertEq(token.balanceOf(owner), token.totalSupply() - 100);\n    }\n\n    function testFailTransferInsufficientBalance() public {\n        vm.prank(user1);\n        token.transfer(user2, 100); // Should fail\n    }\n\n    function testCannotTransferToZeroAddress() public {\n        vm.prank(owner);\n        vm.expectRevert(\"Invalid recipient\");\n        token.transfer(address(0), 100);\n    }\n\n    // Fuzzing test\n    function testFuzzTransfer(uint256 amount) public {\n        vm.assume(amount > 0 && amount <= token.totalSupply());\n\n        vm.prank(owner);\n        token.transfer(user1, amount);\n\n        assertEq(token.balanceOf(user1), amount);\n    }\n\n    // Test with cheatcodes\n    function testDealAndPrank() public {\n        // Give ETH to address\n        vm.deal(user1, 10 ether);\n\n        // Impersonate address\n        vm.prank(user1);\n\n        // Test functionality\n        assertEq(user1.balance, 10 ether);\n    }\n\n    // Mainnet fork test\n    function testForkMainnet() public {\n        vm.createSelectFork(\"https://eth-mainnet.alchemyapi.io/v2/...\");\n\n        // Interact with mainnet contracts\n        address dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;\n        assertEq(IERC20(dai).symbol(), \"DAI\");\n    }\n}\n```\n\n## Advanced Testing Patterns\n\n### Snapshot and Revert\n\n```javascript\ndescribe(\"Complex State Changes\", function () {\n  let snapshotId;\n\n  beforeEach(async function () {\n    snapshotId = await network.provider.send(\"evm_snapshot\");\n  });\n\n  afterEach(async function () {\n    await network.provider.send(\"evm_revert\", [snapshotId]);\n  });\n\n  it(\"Test 1\", async function () {\n    // Make state changes\n  });\n\n  it(\"Test 2\", async function () {\n    // State reverted, clean slate\n  });\n});\n```\n\n### Mainnet Forking\n\n```javascript\ndescribe(\"Mainnet Fork Tests\", function () {\n  let uniswapRouter, dai, usdc;\n\n  before(async function () {\n    await network.provider.request({\n      method: \"hardhat_reset\",\n      params: [\n        {\n          forking: {\n            jsonRpcUrl: process.env.MAINNET_RPC_URL,\n            blockNumber: 15000000,\n          },\n        },\n      ],\n    });\n\n    // Connect to existing mainnet contracts\n    uniswapRouter = await ethers.getContractAt(\n      \"IUniswapV2Router\",\n      \"0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D\",\n    );\n\n    dai = await ethers.getContractAt(\n      \"IERC20\",\n      \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n    );\n  });\n\n  it(\"Should swap on Uniswap\", async function () {\n    // Test with real Uniswap contracts\n  });\n});\n```\n\n### Impersonating Accounts\n\n```javascript\nit(\"Should impersonate whale account\", async function () {\n  const whaleAddress = \"0x...\";\n\n  await network.provider.request({\n    method: \"hardhat_impersonateAccount\",\n    params: [whaleAddress],\n  });\n\n  const whale = await ethers.getSigner(whaleAddress);\n\n  // Use whale's tokens\n  await dai\n    .connect(whale)\n    .transfer(addr1.address, ethers.utils.parseEther(\"1000\"));\n});\n```\n\n## Additional patterns and templates\n\nMore detailed templates and worked examples live in `references/details.md`. Read that file for the full pattern library.","author":"@wshobson","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/wshobson/agents/tree/main/plugins/blockchain-web3/skills/web3-testing","license":"MIT","category":"writing","lang":"en","tokens":1863,"stars":0,"calls30d":2,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"references/details.md","size":1970,"sha256":"790a72d6d2ded6c3b178daf87350156e2283d25c146be8a898ef87185a3ad1d8"}],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":["eth-mainnet.alchemyapi.io"]}}