ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [SmartContract] Truffle μ—μ„œ Solidity Test Code μž‘μ„±ν•˜κΈ°
    SECURITY/Blockchain 2022. 1. 21. 15:35

     

    μ΄λ²ˆμ—λŠ” truffle ν”„λ‘œμ νŠΈμ—μ„œ solidity μ½”λ“œμ— λŒ€ν•œ test code μž‘μ„±ν•˜λŠ” 방법을 μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

    λ‹¨μœ„ ν…ŒμŠ€νŠΈλŠ” javascript λ˜λŠ” solidity 둜 ν…ŒμŠ€νŠΈ 슀크립트λ₯Ό μž‘μ„±ν•˜λ©΄ λ©λ‹ˆλ‹€.

     

    μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ 경우, μžλ°”μŠ€ν¬λ¦½νŠΈ ν…ŒμŠ€νŠΈ ν”„λ ˆμž„μ›Œν¬μΈ mocha λ₯Ό 기반으둜 ν•©λ‹ˆλ‹€.

    νŠΈλŸ¬ν”Œλ‘œ ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜λ©΄, contract λ₯Ό μ»΄νŒŒμΌν•˜κ³  μˆ˜λ™μœΌλ‘œ 배포할 ν•„μš” 없이 migration 으둜 배포된 μ»¨νŠΈλž™νŠΈλ₯Ό μ°Έμ‘°ν•  수 있고, 이더리움 μ£Όμ†Œλ₯Ό ν•˜λ“œμ½”λ”©ν•˜μ§€ μ•Šκ³  μ•”μ‹œμ μœΌλ‘œ 계정을 μ°Έμ‘°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    solidity 둜 ν…ŒμŠ€νŠΈ 슀크립트λ₯Ό μž‘μ„±ν•˜λŠ” 것은 보닀 심화적인 λ‚΄μš©μ΄λΌ, 이 ν¬μŠ€νŒ…μ—μ„œλŠ” μžλ°”μŠ€ν¬λ¦½νŠΈλ‘œ μž‘μ„±ν•˜λŠ” 것을 해보도둝 ν•˜κ² μŠ΅λ‹ˆλ‹€.

     

    1) solidity μ½”λ“œ μž‘μ„±ν•˜κΈ°

    ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•΄ HelloWorld Contract λ₯Ό λ‹€μŒκ³Ό 같이 μž‘μ„±ν•΄μ£Όμ—ˆμŠ΅λ‹ˆλ‹€. (/contracts/HelloWorld.sol)

    pragma solidity >=0.7.0 <0.9.0;
    
    contract HelloWorld {
        string public greet = "Hello World!";
        function echo(string memory _input) external pure returns (string memory){
            return _input;
        }
    }

    greet λ©”μ‹œμ§€κ°€ 있고, echo ν•¨μˆ˜κ°€ μžˆλŠ” κ°„λ‹¨ν•œ contract μž…λ‹ˆλ‹€.

     

    migration 도 μž‘μ„±ν•΄μ€λ‹ˆλ‹€. (/migrations/2_HelloWorld_migration.js)

    const HelloWorld = artifacts.require("HelloWorld");
    
    module.exports = function (deployer) {
        deployer.deploy(HelloWorld);
    };

     

     

    2) test script μž‘μ„±

    ν…ŒμŠ€νŠΈ μ½”λ“œλŠ” truffle ν”„λ‘œμ νŠΈμ˜ test 폴더에 μœ„μΉ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.

     

    λŒ€λΆ€λΆ„ λΉ„λ™κΈ°λ‘œ λ™μž‘ν•˜λ―€λ‘œ, κ²°κ³Όλ₯Ό assert ν•˜λŠ” 뢀뢄도 λΉ„λ™κΈ°μ μœΌλ‘œ μž‘μ„±ν•΄μ•Ό ν•©λ‹ˆλ‹€ (Promise, async/await).

    μ™œ 비동기냐 ν•˜λ©΄μ€, 블둝체인 λ„€νŠΈμ›Œν¬μ— 배포되고 block 이 μƒμ„±λ˜λŠ” λ°μ—λŠ” 지연 μ‹œκ°„μ΄ λ°œμƒν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

    λ”°λΌμ„œ 둜컬 ν…ŒμŠ€νŠΈλ„·μ„ μ΄μš©ν–ˆμ„ λ•Œ μ§€μ—°μ‹œκ°„ 없이 λ™μž‘μ‹œν‚€λ©΄ λΉ„λ™κΈ°λ‘œ μΈν•œ 였λ₯˜κ°€ λ°œμƒν•˜μ§€ μ•Šμ„ 수 μžˆμ΄λ―€λ‘œ, μ‹€μ œ ν™˜κ²½μ—μ„œμ˜ λ™μž‘λ„ κ³ λ €ν•œλ‹€λ©΄ μ§€μ—°μ‹œκ°„μ„ μ„€μ •ν•˜μ—¬ (ganache μ—μ„œ μ„€μ • κ°€λŠ₯) ν…ŒμŠ€νŠΈν•΄λ³΄κΈ°λ₯Ό ꢌμž₯ν•©λ‹ˆλ‹€.

     

    λ¨Όμ € greet λ©”μ‹œμ§€μ— λŒ€ν•œ ν…ŒμŠ€νŠΈλ‘œ ν…ŒμŠ€νŠΈ μ½”λ“œμ˜ ꡬ쑰λ₯Ό μ‚΄νŽ΄λ³΄λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€. (/test/HelloWorldTest.js)

    var hello = artifacts.require("HelloWorld")
    
    contract("HelloWorld", function () {
        it ("is_greet_works_well", async function () {
            const instance = await hello.deployed();
            const response = await instance.greet();
            assert.equal(response, "Hello World", "greeting message is wrong");
        })
    })
    • var hello = artifacts.require("HelloWorld"): HelloWorld contract λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€.
    • contract(): μ»¨νŠΈλž™νŠΈλ₯Ό λ°°ν¬ν•˜κ³  ν…ŒμŠ€νŠΈλ₯Ό μ§„ν•˜λŠ”, 'λ‹¨μœ„ ν…ŒμŠ€νŠΈ 묢음' μž…λ‹ˆλ‹€. 이 λ‚΄λΆ€μ—μ„œλŠ” κ²°κ³Όκ°€ κ³΅μœ λ˜λ―€λ‘œ ν•„μš”ν•˜λ‹€λ©΄ μˆœμ„œλ‚˜ μ—°κ΄€ 관계λ₯Ό κ³ λ €ν•΄μ•Ό ν•©λ‹ˆλ‹€. contract() ν˜ΈμΆœμ€ λͺ¨λ“  μ»¨νŠΈλž™νŠΈκ°€ 이더리움 ν΄λΌμ΄μ–ΈνŠΈμ— λ‹€μ‹œ λ°°ν¬ν•˜λŠ” κ²ƒμœΌλ‘œ, κΉ¨λ—ν•œ μƒνƒœμ—μ„œ ν…ŒμŠ€νŠΈλ₯Ό 진행할 수 μžˆλ„λ‘ ν•©λ‹ˆλ‹€. λ˜ν•œ, 기본적으둜 account[0] 둜 contract λ₯Ό λ°°ν¬ν•˜κ²Œ λ©λ‹ˆλ‹€.
    • it(): ν•˜λ‚˜μ˜ ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€.
    • it ("is_greet_works_well", async function () {}: ν…ŒμŠ€νŠΈ μ½”λ“œ λ©”μ‹œμ§€λ₯Ό 적어주고, ν•΄λ‹Ήν•˜λŠ” ν•¨μˆ˜λ₯Ό μž‘μ„±ν•΄μ€λ‹ˆλ‹€. promise 문법을 μ‚¬μš©ν•΄λ„ λ˜μ§€λ§Œ, μ—¬κΈ°μ„œλŠ” async/await 문법을 μ‚¬μš©ν•˜κΈ° μœ„ν•΄ async function 으둜 μž‘μ„±ν•΄μ£Όμ—ˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ, ν˜„μž¬ HelloWorld μ»¨νŠΈλž™νŠΈλŠ” μ•„λ¬΄λŸ° 생성 parameter κ°€ μ—†μœΌλ―€λ‘œ 인자λ₯Ό μž‘μ„±ν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.
      (주의: async/await λ₯Ό μ‚¬μš©ν•˜λ €λ©΄ Node.js 버전 8.0 μ΄μƒμ—μ„œ μ‹€ν–‰ν•΄μ•Ό ν•©λ‹ˆλ‹€)
    • const instance = await hello.deployed(): HelloWorld μ»¨νŠΈλž™νŠΈκ°€ 배포된 instance λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€.
    • const response = await instance.greet(): greet λ₯Ό μ‹€ν–‰ν•˜κ³  λ°˜ν™˜κ°’μ„ κ°€μ Έμ˜΅λ‹ˆλ‹€. AAA λ ˆμ΄μ•„μ›ƒ (Arrange, Act, Assert) 의 Act 에 ν•΄λ‹Ήν•˜λŠ” λΆ€λΆ„μž…λ‹ˆλ‹€.
    • assert.equal(response, "Hello World!", "greeting message is wrong"): μˆœμ„œλŒ€λ‘œ 비ꡐ할 2가지 κ°’, μ‹€νŒ¨ μ‹œ 좜λ ₯ν•  λ©”μ‹œμ§€λ₯Ό μ μ–΄μ€λ‹ˆλ‹€. μ—¬κΈ°μ„œλŠ” λ°˜ν™˜λœ response 와 μ»¨νŠΈλž™νŠΈμ˜ greet 에 λ„£μ–΄λ‘” "Hello World!", 그리고 μ‹€νŒ¨ μ‹œ 좜λ ₯ν•  "greeting message is wrong" λ₯Ό λ„£μ–΄μ£Όμ—ˆμŠ΅λ‹ˆλ‹€. AAA λ ˆμ΄μ•„μ›ƒ (Arrange, Act, Assert) 의 Assert 에 ν•΄λ‹Ήν•˜λŠ” λΆ€λΆ„μž…λ‹ˆλ‹€.

     

    echo() 에 λŒ€ν•œ ν…ŒμŠ€νŠΈλŠ” 이후 μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

     

     

    3) test

    이제 μž‘μ„±ν•œ ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό ν…ŒμŠ€νŠΈν•΄λ³΄λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

    contract() μ—μ„œλ„ μ•Œ 수 μžˆλ“―μ΄, νŠΈλŸ¬ν”Œμ€ 클린룸 ν…ŒμŠ€νŠΈλ₯Ό μ§€μ›ν•©λ‹ˆλ‹€.

    즉, ν…ŒμŠ€νŠΈ μ½”λ“œ μ‹€ν–‰ 전에 타켓 λ„€νŠΈμ›Œν¬μ— λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ„ μ‹€ν–‰ν•˜μ—¬ μ»¨νŠΈλž™νŠΈλ₯Ό μ²˜μŒλΆ€ν„° λ‹€μ‹œ λ°°ν¬ν•©λ‹ˆλ‹€.

    λ”°λΌμ„œ 개발 μ€‘μ—λŠ” ν…ŒμŠ€νŠΈ λ„€νŠΈμ›Œν¬λ₯Ό μ„€μ •ν•˜μ—¬ λ‹¨μœ„ ν…ŒμŠ€νŠΈλ₯Ό μ‹€ν–‰ν•˜λŠ” 것이 μ’‹κ³ , μ΅œμ’…μœΌλ‘œλŠ” 퍼블릭 λ„€νŠΈμ›Œν¬μ—μ„œ ν…ŒμŠ€νŠΈλ₯Ό μ‹€ν–‰ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

     

    μ—¬κΈ°μ„œλŠ” 이전 ν¬μŠ€νŒ…μ—μ„œ μ„€μ •ν•œ development λ„€νŠΈμ›Œν¬μ—μ„œ μ‹€ν–‰ν•˜λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

    development λŠ” κΈ°λ³Έ λ„€νŠΈμ›Œν¬λ‘œ μΈμ‹ν•˜λ―€λ‘œ λ”°λ‘œ λͺ…μ‹œν•˜μ§€ μ•Šμ•„λ„ λ˜μ§€λ§Œ, λ‹€μŒκ³Ό 같이 λͺ…μ‹œν•˜μ—¬ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    $ truffle test
    $ truffle test --network development // network λͺ…μ‹œ
    $ truffle test ./test/HelloWorldTest.js // 파일 λͺ…μ‹œ

    νŠΉμ • 파일만 μ‹€ν–‰ν•˜κ³  μ‹Άλ‹€λ©΄ 경둜λ₯Ό λͺ…μ‹œν•˜μ—¬ μ‹€ν–‰μ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

     

    ν…ŒμŠ€νŠΈλ₯Ό μ‹€ν–‰μ‹œν‚€λ©΄ λ¨Όμ € solidity μ½”λ“œλ₯Ό μ»΄νŒŒμΌν•˜κ³  ν…ŒμŠ€νŒ…μ„ μ§„ν–‰ν•©λ‹ˆλ‹€.

    λ‹€μŒμ€ μœ„μ— μž‘μ„±ν•œ ν…ŒμŠ€νŠΈ μ½”λ“œμ˜ μ‹€ν–‰ κ²°κ³Όμž…λ‹ˆλ‹€.

     

    Hello World!

    contract 이름과 ν…ŒμŠ€νŠΈ λ‚΄μš©μ΄ 보이고, ν†΅κ³Όλœ 것을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€.

    μ΄λ²ˆμ—λŠ” ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό assert.equal(response, "Hello World", "greeting message is wrong") 둜 λ°”κΏ”μ„œ ν…ŒμŠ€νŠΈ 였λ₯˜μ— μ–΄λ–€ 일이 λ°œμƒν•˜λŠ”μ§€ 보도둝 ν•˜κ² μŠ΅λ‹ˆλ‹€.

     

    Hello World

     

    μ΄λ ‡κ²Œ test κ°€ fail λ˜μ—ˆμŒμ„ 보여주고, assert.equal μ—μ„œ 였λ₯˜ λ©”μ‹œμ§€λ‘œ μž‘μ„±ν•œ λ‚΄μš© ("greeting message is wrong") 이 보이며 μ˜ˆμƒν•œ λ‚΄μš© (2번째 인자, Hello World) 와 μ‹€μ œ λ‚΄μš© (Hello World!) 을 λ³΄μ—¬μ€λ‹ˆλ‹€.

     

     

    +) μΆ”κ°€λ‘œ ν…ŒμŠ€νŠΈ μ½”λ“œ μž‘μ„± 및 μ‹€ν–‰

    μ΄λ²ˆμ—λŠ” HelloWorld μ»¨νŠΈλž™νŠΈμ— 있던 echo() ν•¨μˆ˜λ₯Ό ν…ŒμŠ€νŠΈν•΄λ³΄λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

    ν…ŒμŠ€νŠΈ μ½”λ“œλŠ” λ‹€μŒκ³Ό 같이 Promise λ₯Ό μ‚¬μš©ν•œ 것과 async/await λ₯Ό μ‚¬μš©ν•œ 것을 λ‘˜ λ‹€ μž‘μ„±ν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

    async/await 둜 μž‘μ„±ν•˜λ©΄ μ½”λ“œμ˜ 가독성이 λ†’μ•„μ§€λŠ” 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

    var hello = artifacts.require("HelloWorld")
    
    contract("HelloWorld", function () {
        it ("is_echo_works_well_async", async function () {
            const instance = await hello.deployed();
            const message = "testing";
            const response = await instance.echo(message);
            assert.equal(response, message);
        })
        it ("is_echo_works_well_promise", function () {
            return hello.deployed().then(function(instance) {
                const message = "testing";
                instance.echo(message).then(function (response) {
                    assert.equal(response, message);
                })
            })
        })
    })

    이제 HelloWorldTest.js μ—λŠ” μœ„μ˜ greet ν…ŒμŠ€νŠΈλ₯Ό 합쳐 3가지 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€κ°€ μžˆμŠ΅λ‹ˆλ‹€. (λ¬Όλ‘  2κ°œλŠ” μž‘μ„± λ°©μ‹λ§Œ λ‹€λ₯Έ κ²ƒμ΄μ§€λ§Œ...)

     

    이λ₯Ό 싀행해보면 λ‹€μŒκ³Ό 같은 κ²°κ³Όλ₯Ό 얻을 수 μžˆμŠ΅λ‹ˆλ‹€.

     

     

     

     

    μ΄κ²ƒμœΌλ‘œ νŠΈλŸ¬ν”Œμ—μ„œ 솔리디티 ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” 방법을 μ‚΄νŽ΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

     

    λŒ“κΈ€

Designed by Tistory.