스마트 컨트랙트 Testing Guide 이 장에서는 스마트 컨트랙트를 테스트하는 방법을 소개합니다. 블록체인의 트랜잭션은 되돌릴 수 없으므로 스마트 컨트랙트를 배포하기 전에 테스트하는 것이 매우 중요합니다.
트러플(Truffle)로 테스트하기
트러플은 자동 테스트 프레임워크를 제공합니다. 이 프레임워크를 사용하여 간단하고 관리 가능한 테스트를 작성할 수 있는 방법이 두 가지 있습니다.
Javascript
및 TypeScript
를 이용하여 블록체인 외부에서 컨트랙트를 실행하는 애플리케이션처럼 작성
Solidity
를 활용하여 컨트랙트 함수를 직접 호출
1) 시작하기
배포하기 전에 스마트 컨트랙트 테스트를 위해 값 설정 함수 setGreet
함수를 추가합니다. 소스 코드는 아래와 같습니다.
Copy pragma solidity ^0.5.6;
contract Mortal {
/* 주소 타입의 소유자(owner) 변수 정의 */
address payable owner;
/* 이 함수는 초기화 시점에 실행되어 컨트랙트 소유자를 설정합니다 */
constructor () public { owner = msg.sender; }
/* 컨트랙트에서 자금을 회수하는 함수 */
function kill () public payable { if (msg.sender == owner) selfdestruct (owner); }
}
contract MEVerseGreeter is Mortal {
/* 문자열 타입의 변수 greeting 정의 */
string greeting;
/* 이 함수는 컨트랙트가 실행될 때 작동합니다 */
constructor ( string memory _greeting ) public {
greeting = _greeting;
}
/* 주(Main) 함수 */
function greet () public view returns ( string memory ) {
return greeting;
}
/* 테스트를 위해 새로 추가된 함수입니다 */
function setGreet ( string memory _greeting ) public {
// 소유자(owner)만 greeting 메세지를 수정할 수 있습니다
require (msg.sender == owner , "Only owner is allowed." );
greeting = _greeting;
}
}
테스트 시나리오
greet()
함수가 "Hello, MEVerse"이라는 메세지를 잘 출력하는지
setGreet()
함수가 새로 설정된 greeting 메세지를 잘 출력하고 소유자가 아닌 계정이 greeting을 업데이트하려고 할 때 revert를 하는지
먼저 일반적인 어설션(assertions)을 위해 Chai 어설션 라이브러리를 설치하고 (또는 사용하고 있는 다른 어설션 라이브러리도 괜찮습니다), 스마트 컨트랙트 어설션을 위해 truffle-assertions 라이브러리를 설치합니다.
Copy npm install --save-dev chai truffle-assertions
2) 솔리디티로 테스트 작성하기
솔리디티로 테스트하는 것은 자바스크립트로 테스트하는 것보다 조금 더 직관적일 수 있습니다. 솔리디티 테스트 컨트랙트는 자바스크립트 테스트와 함께 .sol 파일로 제공됩니다.
test
폴더에 TestMEVerseGreeting.sol
이름의 파일을 생성합니다. 트러플 제품군은 테스트를 위한 헬퍼(helper) 라이브러리를 제공하므로, 이들을 불러옵니다. 솔리디티 테스트 예시를 살펴봅시다.
Copy pragma solidity ^0.5.6;
import "truffle/Assert.sol" ;
import "truffle/DeployedAddresses.sol" ;
import "../contracts/HashMarket.sol" ;
Assert : Assert.equals()
, Assert.greaterThan()
등과 같은 다양한 테스트 함수에 액세스할 수 있도록 합니다.
DeployedAddresses : 컨트랙트를 변경할 때마다, 반드시 새 주소로 재배포해야 합니다. 이 라이브러리를 통해 배포된 컨트랙트 주소를 얻을 수 있습니다.
Copy pragma solidity ^0.5.6;
import "truffle/Assert.sol" ;
import "truffle/DeployedAddresses.sol" ;
import "../contracts/MEVerseGreeter.sol" ;
contract TestMEVerseGreeting {
function testGreetingMessage () public {
// DeployedAddresses.MEVerseGreeter()는 컨트랙트 주소를 다룹니다.
MEVerseGreeter greeter = MEVerseGreeter (DeployedAddresses. MEVerseGreeter ());
string memory expectedGreet = "Hello MEVerse" ;
string memory greet = greeter. greet ();
Assert. equal (greet , expectedGreet , "greeting message should match" );
}
}
솔리디티 테스트 코드를 실행합니다.
Copy $ truffle test
Using network 'test' .
Compiling your contracts...
===========================
> Compiling ./test/TestMEVerseGreeting.sol
> Artifacts written to /var/folders/8k/pj9b6lld4qn4hq1l7h97y9pm0000gn/T/test--5158-mS50YzRSpyUZ
> Compiled successfully using:
- solc: 0.5.16+commit.9c3226ce.Emscripten.clang
TestMEVerseGreeting
1 ) testGreetingMessage
> No events were emitted
0 passing (3s)
1 failing
1 ) TestMEVerseGreeting
testGreetingMessage:
Error: greeting message should match (Tested: Hello, MEVerse, Against: Hello MEVerse )
at checkResultForFailure (/Users/meverselabs/.nvm/versions/node/v16.16.0/lib/node_modules/truffle/build/webpack:/packages/core/lib/testing/SolidityTest.js:66:1)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at Context. < anonymou s > (/Users/meverselabs/.nvm/versions/node/v16.16.0/lib/node_modules/truffle/build/webpack:/packages/core/lib/testing/SolidityTest.js:92:1)
테스트를 실패했습니다.
Error: greeting message should match (Tested: Hello, MEVerse, Against: Hello MEVerse)
배포컨트랙트는 Hello, MEVerse이고 테스트로 작성한 코드는 Hello MEVerse ',(comma)' 로 동일하지 않아서 테스트 실패하였습니다. 테스트 코드 수정(추가)후 테스트 해보겠습니다.
Copy $ truffle test
Using network 'test' .
Compiling your contracts...
===========================
> Compiling ./test/TestMEVerseGreeting.sol
> Artifacts written to /var/folders/8k/pj9b6lld4qn4hq1l7h97y9pm0000gn/T/test--5231-DBNS4Mgo7Mo0
> Compiled successfully using:
- solc: 0.5.16+commit.9c3226ce.Emscripten.clang
TestMEVerseGreeting
✓ testGreetingMessage (41ms)
1 passing (3s)
테스트가 성공적으로 통과 된 것을 확인합니다.
테스트넷에서 컨트랙트를 테스트하고 싶으면 network 옵션
을 사용하면 됩니다.
Copy $ truffle test -network MEVerseTestnet
Using network 'test' .
Compiling your contracts...
===========================
> Compiling ./contracts/MEVerseGreeter.sol
> Compiling ./test/TestMEVerseGreeting.sol
> Artifacts written to /var/folders/8k/pj9b6lld4qn4hq1l7h97y9pm0000gn/T/test--5324-jtF8M5Qz82cV
> Compiled successfully using:
- solc: 0.5.16+commit.9c3226ce.Emscripten.clang
TestMEVerseGreeting
✓ testGreetingMessage (55ms)
1 passing (3s)
3) 자바스크립트로 테스트 작성하기
트러플은 자바스크립트 테스트를 위한 견고한 프레임워크를 제공하기 위해 Mocha 테스트 프레임워크 및 Chai 어설션 라이브러리를 사용합니다. 자바스크립트 테스트는 더 많은 유연성을 제공하며 더 복잡한 테스트를 작성할 수 있게 합니다.
test
폴더에 0_MEVerseGreeting.js
이름의 파일을 생성하고 다음의 테스트 코드를 추가합니다.
Copy // MEVerseGreeter 컨트랙트와 직접 상호작용
const MEVerseGreeter = artifacts .require ( "./MEVerseGreeter.sol" );
const truffleAssert = require ( 'truffle-assertions' );
contract ( "MEVerseGreeter" , async (accounts) => {
// 컨트랙트 인스턴스를 상위 레벨에 저장해
// 모든 함수에서 접근할 수 있도록 합니다.
var MEVerseGreeterInstance;
var owner = accounts[ 0 ];
var greetMsg = "Hello, MEVerse" ;
// 각 테스트가 진행되기 전에 실행됩니다.
before ( async function () {
// set contract instance into a variable
MEVerseGreeterInstance = await MEVerseGreeter .new (greetMsg , {from : owner});
})
it ( "#1 check Greeting message" , async function () {
// set the expected greeting message
var expectedGreeting = greetMsg;
var greet = await MEVerseGreeterInstance .greet ();
assert .equal (expectedGreeting , greet , "greeting message should match" );
})
it ( "#2 update greeting message." , async function () {
var newGreeting = "Hi, MEVerse" ;
await MEVerseGreeterInstance .setGreet (newGreeting , { from : owner });
var greet = await MEVerseGreeterInstance .greet ();
assert .equal (newGreeting , greet , "greeting message should match" );
});
it ( "#3 [Failure test] Only owner can change greeting." , async function () {
var fakeOwner = accounts[ 1 ];
await truffleAssert .fails ( MEVerseGreeterInstance .setGreet (greetMsg , { from : fakeOwner }));
});
});
테스트 파일(0_MEVerseGreeting.js
)을 지정하여 테스트를 실행합니다.
Copy $ truffle test ./test/0_MEVerseGreeting.js
Using network 'test' .
Compiling your contracts...
===========================
> Compiling ./contracts/MEVerseGreeter.sol
> Artifacts written to /var/folders/8k/pj9b6lld4qn4hq1l7h97y9pm0000gn/T/test--5527-DYxpp0UInYnm
> Compiled successfully using:
- solc: 0.5.16+commit.9c3226ce.Emscripten.clang
Contract: MEVerseGreeter
✓ #1 check Greeting message
✓ #2 update greeting message. (42ms)
✓ #3 [Failure test] Only owner can change greeting. (176ms)
3 passing (301ms)
테스트에 대한 자세한 내용은 Truffle testing 및 Truffle commands 을 참조하시길 바랍니다.