hypothesis
프레임워크를 사용합니다.이 섹션의 많은 내용은 공식 hypothesis.works 웹사이트를 기반으로 합니다. 속성 기반 테스트에 대해 더 알아보려면 이 소개 글 시리즈를 읽거나 공식 Hypothesis 문서를 참조할 수 있습니다.
속성 기반 테스트란?
속성 기반 테스트는 코드 내에서 엣지 케이스를 찾고 잘못된 가정을 발견하는 강력한 도구입니다.
속성 기반 테스트의 핵심 개념은 단일 시나리오에 대한 테스트를 작성하는 대신, 여러 시나리오를 설명하는 테스트를 작성하고 컴퓨터가 각 경우의 가능성을 탐색하도록 하는 것입니다. 이를 수작업으로 일일이 작성하는 대신 컴퓨터가 자동으로 수행합니다.
기본적인 프로세스는 다음과 같습니다:
- 스마트 계약 내에서 테스트하려는 함수를 선택합니다.
- 항상 동일한 결과를 내야하는 이 함수에 대한 입력 범위를 지정합니다.
- 명세서에서 무작위 데이터를 사용하여 함수를 호출합니다.
- 결과에 대한 어설션을 작성합니다.
이 기술을 사용하면 각 테스트가 다양한 임의 데이터를 사용하여 여러 번 실행됩니다. 어설션이 실패하는 예가 발견되면 문제를 일으키는 가장 간단한 케이스를 찾으려고 시도합니다. 그런 다음 이 예제는 데이터베이스에 저장되어 문제가 해결되면 계속 고정됩니다.
테스트 작성
속성 기반 테스트 작성을 시작하려면 다음 두 가지 메소드를 가져와야 합니다:
from brownie.test import given, strategy
brownie.test.given
()
인수를 받는 테스트 함수를 무작위 테스트로 바꾸기 위한 데코레이터입니다. Brownie를 사용할 때 이것은 속성 기반 테스트의 주요 진입점입니다. 이것은 hypothesis.given의 얇은 래퍼입니다. API는 동일합니다.
@given
을 임포트해야 합니다. 함수를 직접 임포트하면 테스트 격리에 문제가 발생할 수 있습니다.brownie.test.strategy
()
ABI 유형을 기반으로 하는 테스트 전략을 생성하는 방법.
Hypothesis를 사용한 테스트는 일반 pytest 테스트와 유사한 함수와 추가 인수가 포함된 @given
데코레이터로 구성됩니다.
다음은 ERC20 토큰 계약의 transfer
기능을 테스트하는 기본 예제입니다.
from brownie import accounts
from brownie.test import given, strategy
@given(value=strategy('uint256', max_value=10000))
def test_transfer_amount(token, value):
balance = token.balanceOf(accounts[0])
token.transfer(accounts[1], value, {'from': accounts[0]})
assert token.balanceOf(accounts[0]) == balance - value
이 테스트가 실행될 때 다음과 같은 과정이 진행됩니다:
- 모든 pytest fixture의 설정 단계는 일반적인 순서대로 실행됩니다.
- 현재 체인 상태의 스냅샷이 촬영됩니다.
- strategy는 무작위 정수 값을 생성하고 amount 키워드 인자에 할당합니다.
- 테스트가 실행됩니다.
- 체인은 단계 2에서 촬영한 스냅샷으로 되돌립니다.
- 단계 3에서 5를 50번 또는 테스트가 실패할 때까지 반복합니다.
- 모든 pytest fixture의 해제 단계는 일반적인 순서대로 실행됩니다.
@given
을 사용하여 여러 전략을 제공할 수 있습니다. 다음 예제에서는 주소 전략을 사용하여 to
인자를 추가합니다.
from brownie import accounts
from brownie.test import given, strategy
@given(
to=strategy('address', exclude=accounts[0]),
value=strategy('uint256', max_value=10000),
)
def test_transfer_amount(token, to, value):
balance = token.balanceOf(accounts[0])
token.transfer(to, value, {'from': accounts[0]})
assert token.balanceOf(accounts[0]) == balance - value
assert token.balanceOf(to) == value
전략
모든 테스트의 핵심 객체는 전략입니다. 전략은 생성하려는 데이터 유형을 설명하기 위한 레시피입니다. Brownie는 주어진 ABI 유형에 대한 전략을 생성하는 strategy
메서드를 제공합니다.
>>> from brownie.test import strategy
>>> strategy('uint8')
integers(min_value=0, max_value=255)
각 전략 객체는 생성될 데이터 유형을 탐색하기 위해 콘솔에서 호출할 수 있는 example
메소드를 포함합니다.
>>> st = strategy('uint8')
>>> st.example()
243
>>> st.example()
77
strategy
는 ABI 유형에 따라 다른 키워드 인자를 받습니다.
유형 전략
다음 전략들은 Solidity와 Vyper의 유형에 해당합니다.
Address
기본 전략: hypothesis.strategies.sampled_from
address
전략은 Accounts
컨테이너의 Account
객체를 생성합니다.
옵션 키워드 인자:
- length: 전략에 포함되는 Account 객체의 수입니다. 만약 Accounts 컨테이너가 이 수보다 적은 경우, 전체 컨테이너가 사용됩니다.
- excludes: 전략 결과를 필터링하는 데 사용되는 객체, 이터러블 또는 콜러블입니다.
>>> strategy('address')
sampled_from(accounts)
>>> strategy('address').example()
<Account '0x33A4622B82D4c04a53e170c638B944ce27cffce3'>
Bool
기본 전략: hypothesis.strategies.booleans
bool
전략은 True
또는 False
를 반환합니다.
이 전략은 키워드 인자를 받지 않습니다.
>>> strategy('bool')
booleans()
>>> strategy('bool').example()
True
바이트
기본 전략: hypothesis.strategies.binary
bytes
전략은 바이트 문자열을 생성합니다.
모든 bytes
전략은 다음의 키워드 인자를 받습니다:
- excludes: 결과를 필터링하는 데 사용되는 객체, iterable 또는 callable입니다.
고정 길이 값 (bytes1
... bytes32
)의 경우 전략은 항상 지정된 길이의 바이트를 생성합니다. 동적 바이트 배열 (bytes
)의 경우 최소 및 최대 길이를 다음과 같은 키워드 인자를 사용하여 지정할 수 있습니다:
- min_size: 반환되는 각 값의 최소 길이입니다. 기본값은 1입니다.
- max_size: 반환되는 각 값의 최대 길이입니다. 기본값은 64입니다.
>>> strategy('bytes32')
binary(min_size=32, max_size=32)
>>> strategy('bytes', max_size=16)
binary(min_size=1, max_size=16)
>>> strategy('bytes8').example()
b'\xb8\xd6\xaa\xcbR\x0f\xb88'
Decimal
기본 전략: hypothesis.strategies.decimals
decimal
전략은 decimal.Decimal
인스턴스를 생성합니다.
선택적 키워드 인자:
- min_value: 반환할 최솟값. 기본값은 -2**127 (Vyper의 decimal 타입의 하한)입니다. 지정된 값은 Fixed로 변환됩니다.
- max_value: 반환할 최댓값. 기본값은 2**127-1 (Vyper의 decimal 타입의 상한)입니다. 지정된 값은 Fixed로 변환됩니다.
- places: 포함할 소수점의 개수입니다. 기본값은 10입니다.
- excludes: 전략 결과를 필터링하는 데 사용되는 객체, iterable 또는 callable입니다.
>>> strategy('decimal')
decimals(min_value=-170141183460469231731687303715884105728, max_value=170141183460469231731687303715884105727, places=10)
>>> strategy('decimal').example()
Decimal('44.8234019327')
정수
기본 전략: hypothesis.strategies.integers
int
및 uint
전략은 정수 값을 반환합니다.
선택적 키워드 인수:
- min_value: 반환할 최소값입니다. 기본값은 지정된 유형의 하한입니다. 지정된 값은 Wei로 변환됩니다.
- max_value: 반환할 최대값입니다. 기본값은 지정된 유형의 상한입니다. 지정된 값은 Wei로 변환됩니다.
- excludes: 전략 결과를 필터링하는 데 사용되는 개체, 반복 가능한 또는 호출 가능한 객체입니다.
>>> strategy('uint32')
integers(min_value=0, max_value=4294967295)
>>> strategy('int8')
integers(min_value=-128, max_value=127)
>>> strategy('uint', min_value="1 ether", max_value="25 ether")
integers(min_value=1000000000000000000, max_value=25000000000000000000)
>>> strategy('uint').example()
156806085
문자열
기본 전략: hypothesis.strategies.text
string
전략은 유니코드 텍스트 문자열을 반환합니다.
옵션 키워드 인자:
- min_size: 각 반환 값의 최소 길이입니다. 기본값은 0입니다.
- max_size: 각 반환 값의 최대 길이입니다. 기본값은 64입니다.
- excludes: 전략 결과를 필터링하는 데 사용되는 객체, 반복 가능한 항목 또는 호출 가능 항목입니다.
>>> strategy('string')
text(max_size=64)
>>> strategy('string', min_size=12, max_size=23)
text(min_size=12, max_size=23)
>>> strategy('string').example()
'\x02\x14\x01\U0009b3c5'
시퀀스 전략
핵심 전략과 함께, 브라우니는 배열 또는 튜플 시퀀스를 생성하는 전략도 제공합니다.
배열
기본 전략: hypothesis.strategies.lists
배열 전략은 기본 배열 유형의 전략 목록을 생성합니다. 고정 길이 및 동적 길이의 배열 및 다차원 배열을 생성할 수 있습니다.
선택적 키워드 인수:
- min_length: 동적 배열 내의 최소 아이템 수입니다. 기본값은 1입니다.
- max_length: 동적 배열 내의 최대 아이템 수입니다. 기본값은 8입니다.
- unique: True인 경우 목록의 각 항목은 고유합니다.
다차원 동적 배열의 경우, min_length
및 max_length
는 동적 차원 수와 같은 길이의 리스트로 제공될 수 있습니다.
또한 배열의 기본 유형에 대한 키워드 인수를 포함할 수 있습니다. 생성된 목록 내의 모든 항목에 적용됩니다.
>>> strategy('uint32[]')
lists(elements=integers(min_value=0, max_value=4294967295), min_length=1, max_length=8)
>>> strategy('uint[3]', max_value=42)
lists(elements=integers(min_value=0, max_value=42), min_length=3, max_length=3)
>>> strategy('uint[3]', max_value=42).example()
[16, 23, 14]
튜플
기본 전략: hypothesis.strategies.tuples
튜플 전략은 지정된 타입 문자열에 따라 혼합 전략의 튜플을 생성합니다.
이 전략은 키워드 인자를 허용하지 않습니다.
>>> strategy('(int16,bool)')
tuples(integers(min_value=-32768, max_value=32767), booleans())
>>> strategy('(uint8,(bool,bytes4))')
tuples(integers(min_value=0, max_value=255), tuples(booleans(), binary(min_size=4, max_size=4)))
>>> strategy('(uint16,bool)').example()
(47628, False)
계약 전략
contract_strategy
함수는 ContractContainer
내의 ProjectContract
객체에서 끌어오는 데 사용됩니다.
brownie.test.contract_strategy
(contract_name)
기본 전략: hypothesis.strategies.sampled_from
ProjectContract
객체에 액세스하는 전략입니다. • contract_name
: 문자열로 지정된 계약의 이름입니다.
>>> ERC20
[<ERC20 Contract '0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87'>, <ERC20 Contract '0x602C71e4DAC47a042Ee7f46E0aee17F94A3bA0B6'>]
>>> from brownie.test import contract_strategy
>>> contract_strategy('ERC20')
sampled_from(ERC20)
>>> contract_strategy('ERC20').example()
<ERC20 Contract '0x602C71e4DAC47a042Ee7f46E0aee17F94A3bA0B6'>
기타 전략
Brownie가 제공하는 모든 전략은 hypothesis.strategies
라이브러리의 핵심 전략을 기반으로 합니다. Brownie가 제공하는 것보다 더 구체적이거나 복잡한 것이 필요하다면, 직접 hypothesis 전략을 사용할 수도 있습니다.
사용 가능한 전략과 그들이 어떻게 사용자 정의될 수 있는지에 대한 자세한 내용은 Hypothesis 전략 문서를 참조하십시오.
설정
프로퍼티 기반 테스트가 실행되는 방식에 대한 기본 설정을 수정해야 할 수도 있습니다.
이를 수행하는 메커니즘은 hypothesis.settings
객체입니다. 설정 데코레이터를 사용하여 @given
기반의 테스트를 이를 사용하도록 설정할 수 있습니다:
from brownie.test import given
from hypothesis import settings
@given(strategy('uint256'))
@settings(max_examples=500)
def test_this_thoroughly(x):
pass
프로젝트의 brownie-config.yaml
파일에 hypothesis
필드를 추가하여 설정을 영구적으로 변경할 수도 있습니다:
hypothesis:
max_examples: 500
자세한 내용은 :ref:설정 파일<config>
문서를 참조하십시오.
사용 가능한 설정
deadline
각 테스트 내 개별 예제가 실행될 수 있는 밀리초 수입니다. 이 시간보다 긴 시간이 걸리는 테스트는 실패한 것으로 간주됩니다. Brownie 테스트 시간은 크게 다양할 수 있기 때문에 이 속성은 기본적으로 비활성화되어 있습니다. 기본값: None
max_examples
테스트가 통과될 때까지 실행될 수 있는 최대 횟수입니다. 많은 복잡한 거래가 포함된 테스트의 경우 이 값을 줄이는 것이 좋습니다. 기본값: 50
report_multiple_bugs
Hypothesis는 각 테스트를 여러 번 실행하므로 때로는 한 번 실행에서 여러 버그를 찾을 수 있습니다. 이를 모두 보고하는 것은 유용할 수 있지만, 단일 오류를 보고할 때보다 훨씬 더 길고 구체적이지 않은 출력물을 만들어냅니다. 기본값: False
stateful_step_count
상태 기계에서 실행할 수 있는 규칙의 최대 수입니다. 이 수를 초과하면 실행을 중지하고 테스트가 통과된 것으로 간주합니다. 보다 복잡한 상태 기계의 경우 이 값을 증가시키는 것이 좋지만, 실행 시간이 길어질 수 있다는 것을 염두에 두어야 합니다. 기본값: 10
'블록체인 (Block Chain) > 이더리움' 카테고리의 다른 글
[브라우니 (Brownie)] 19. 커버리지 평가 (0) | 2023.06.18 |
---|---|
[브라우니 (Brownie)] 18. 상태 기반 테스트 (1) | 2023.06.18 |
[브라우니 (Brownie)] 16. 픽스처 및 마커 참조 (1) | 2023.06.18 |
[브라우니 (Brownie)] 15. 단위 테스트 작성하기 (0) | 2023.06.18 |
[브라우니 (Brownie)] 14. 데이터 유형 (0) | 2023.06.16 |