Test Markers¶
Test markers are used to categorize tests and to run specific subsets of tests. They are defined in the test files using the pytest.mark decorator.
The examples below use StateTestFiller tests, but the same markers can also be applied to BlockchainTestFiller tests.
Fork Markers¶
These markers are used to specify the forks for which a test is valid.
@pytest.mark.valid_from("FORK_NAME")¶
Bases: ValidityMarker
Marker used to specify the fork from which the test is valid. The test will not be filled for forks before the specified fork.
import pytest
from execution_testing import Alloc, StateTestFiller
@pytest.mark.valid_from("London")
def test_something_only_valid_after_london(
state_test: StateTestFiller,
pre: Alloc
):
pass
In this example, the test will only be filled for the London fork and after, e.g. London, Paris, Shanghai, Cancun, etc.
Source code in packages/testing/src/execution_testing/cli/pytest_commands/plugins/forks/forks.py
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 | |
@pytest.mark.valid_until("FORK_NAME")¶
Bases: ValidityMarker
Marker to specify the fork until which the test is valid. The test will not be filled for forks after the specified fork.
import pytest
from execution_testing import Alloc, StateTestFiller
@pytest.mark.valid_until("London")
def test_something_only_valid_until_london(
state_test: StateTestFiller,
pre: Alloc
):
pass
In this example, the test will only be filled for the London fork and before, e.g. London, Berlin, Istanbul, etc.
Source code in packages/testing/src/execution_testing/cli/pytest_commands/plugins/forks/forks.py
945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 | |
@pytest.mark.valid_before("FORK_OR_EIP")¶
Bases: ValidityMarker
Marker to specify the fork or EIP before which the test is valid.
The test will be filled for all forks strictly before the specified fork — the fork itself is excluded.
valid_before vs valid_until:
valid_until("Prague")— inclusive: runs through Prague.valid_before("EIP7825")— exclusive: runs up to but not at the point where EIP-7825 activates.
import pytest
from execution_testing import Alloc, StateTestFiller
@pytest.mark.valid_before("EIP7825")
def test_something_only_valid_before_eip7825(
state_test: StateTestFiller,
pre: Alloc
):
pass
In this example, the test will only be filled for forks where EIP-7825 is not yet active.
Source code in packages/testing/src/execution_testing/cli/pytest_commands/plugins/forks/forks.py
980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 | |
@pytest.mark.valid_at("FORK_NAME_1", "FORK_NAME_2", ...)¶
Bases: ValidityMarker
Marker to specify each fork individually for which the test is valid.
import pytest
from execution_testing import Alloc, StateTestFiller
@pytest.mark.valid_at("London", "Cancun")
def test_something_only_valid_at_london_and_cancun(
state_test: StateTestFiller,
pre: Alloc
):
pass
In this example, the test will only be filled for the London and Cancun forks.
Source code in packages/testing/src/execution_testing/cli/pytest_commands/plugins/forks/forks.py
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 | |
@pytest.mark.valid_at_transition_to("FORK_NAME")¶
Important
Tests using this marker must type their fork parameter as TransitionFork (imported from execution_testing) instead of the regular Fork type. The TransitionFork type provides transition-specific methods such as fork_at(), transitions_to(), and transitions_from(). See Transition Fork Tests for details.
Bases: ValidityMarker
Marker to specify that a test is only meant to be filled at the transition to the specified fork.
The test usually starts at the fork prior to the specified fork at genesis and at block 5 (for pre-merge forks) or at timestamp 15,000 (for post-merge forks) the fork transition occurs.
import pytest
from execution_testing import Alloc, BlockchainTestFiller
@pytest.mark.valid_at_transition_to("London")
def test_something_that_happens_during_the_fork_transition_to_london(
blockchain_test: BlockchainTestFiller,
pre: Alloc
):
pass
In this example, the test will only be filled for the fork that transitions
to London at block number 5, BerlinToLondonAt5, and no other forks.
To see or add a new transition fork, see the
execution_testing.forks.forks.transition module.
Note that the test uses a BlockchainTestFiller fixture instead of a
StateTestFiller, as the transition forks are used to test changes
throughout the blockchain progression, and not just the state change of a
single transaction.
This marker also accepts the following keyword arguments:
subsequent_transitions: Force the test to also fill for subsequent fork transitions.until: Impliessubsequent_transitionsand puts a limit on which transition fork will the test filling will be limited to.
For example:
@pytest.mark.valid_at_transition_to("Cancun", subsequent_transitions=True)
produces tests on ShanghaiToCancunAtTime15k and
CancunToPragueAtTime15k, and any transition fork after that.
And:
@pytest.mark.valid_at_transition_to("Cancun",
subsequent_transitions=True, until="Prague")
produces tests on ShanghaiToCancunAtTime15k and
CancunToPragueAtTime15k, but no forks after Prague.
Source code in packages/testing/src/execution_testing/cli/pytest_commands/plugins/forks/forks.py
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 | |
Fork Covariant Markers¶
These markers are used in conjunction with the fork validity markers to automatically parameterize tests with values that are valid for the fork being tested.
@pytest.mark.with_all_tx_types¶
This marker is used to automatically parameterize a test with all transaction types that are valid for the fork being tested.
import pytest
from execution_testing.tools import Alloc, StateTestFiller
@pytest.mark.with_all_tx_types
@pytest.mark.valid_from("Berlin")
def test_something_with_all_tx_types(
state_test: StateTestFiller,
pre: Alloc,
tx_type: int
):
pass
In this example, the test will be parameterized for parameter tx_type with values [0, 1] for fork Berlin, but with values [0, 1, 2] for fork London (because of EIP-1559).
@pytest.mark.with_all_contract_creating_tx_types¶
This marker is used to automatically parameterize a test with all contract creating transaction types that are valid for the fork being tested.
This marker only differs from pytest.mark.with_all_tx_types in that it does not include transaction type 3 (Blob Transaction type) on fork Cancun and after.
@pytest.mark.with_all_typed_transactions¶
This marker is used to automatically parameterize a test with all typed transactions, including type=0 (legacy transaction), that are valid for the fork being tested.
This marker is an indirect marker that utilizes the tx_type values from the pytest.mark.with_all_tx_types marker to build default typed transactions for each tx_type.
Optional: Default typed transactions used as values for typed_transaction exist in packages/testing/src/execution_testing/cli/pytest_commands/plugins/shared/transaction_fixtures.py and can be overridden for the scope of
the test by re-defining the appropriate pytest.fixture for that transaction type.
import pytest
from execution_testing.tools import Account, Alloc, StateTestFiller
from execution_testing.test_types import Transaction
# Optional override for type 2 transaction
@pytest.fixture
def type_2_default_transaction(sender: Account):
return Transaction(
ty=2,
sender=sender,
max_fee_per_gas=0x1337,
max_priority_fee_per_gas=0x1337,
...
)
# Optional override for type 4 transaction
@pytest.fixture
def type_4_default_transaction(sender: Account, pre: Alloc):
return Transaction(
ty=4,
sender=sender,
...,
authorization_list=[
AuthorizationTuple(
address=Address(1234),
nonce=0,
chain_id=1,
signer=pre.fund_eoa(),
)
]
)
@pytest.mark.with_all_typed_transactions
@pytest.mark.valid_from("Prague")
def test_something_with_all_tx_types(
state_test: StateTestFiller,
pre: Alloc,
typed_transaction: Transaction
):
pass
@pytest.mark.with_all_precompiles¶
This marker is used to automatically parameterize a test with all precompiles that are valid for the fork being tested.
import pytest
from execution_testing.tools import Alloc, StateTestFiller
@pytest.mark.with_all_precompiles
@pytest.mark.valid_from("Shanghai")
def test_something_with_all_precompiles(
state_test: StateTestFiller,
pre: Alloc,
precompile: int,
):
pass
In this example, the test will be parameterized for parameter precompile with values [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] for fork Shanghai, but with values [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] for fork Cancun which introduced the point evaluation precompile defined in EIP-4844.
@pytest.mark.with_all_call_opcodes¶
This marker is used to automatically parameterize a test with all EVM call opcodes that are valid for the fork being tested.
import pytest
from execution_testing.tools import Alloc, StateTestFiller
from execution_testing.vm import Opcodes as Op
@pytest.mark.with_all_call_opcodes
@pytest.mark.valid_from("Frontier")
def test_something_with_all_call_opcodes(
state_test: StateTestFiller,
pre: Alloc,
call_opcode: Op
):
pass
In this example, the test will be parametrized for parameter call_opcode with values [Op.CALL, Op.CALLCODE] starting on fork Frontier, [Op.CALL, Op.CALLCODE, Op.DELEGATECALL] on fork Homestead, and [Op.CALL, Op.CALLCODE, Op.DELEGATECALL, Op.STATICCALL] on fork Byzantium and later.
@pytest.mark.with_all_create_opcodes¶
This marker is used to automatically parameterize a test with all EVM create opcodes that are valid for the fork being tested.
import pytest
from execution_testing.tools import Alloc, StateTestFiller
from execution_testing.vm import Opcodes as Op
@pytest.mark.with_all_create_opcodes
@pytest.mark.valid_from("Frontier")
def test_something_with_all_create_opcodes(
state_test: StateTestFiller,
pre: Alloc,
create_opcode: Op
):
pass
In this example, the test will be parametrized for parameter create_opcode with values [Op.CREATE] starting on fork Frontier, and [Op.CREATE, Op.CREATE2] starting on fork Constantinople and later.
@pytest.mark.with_all_system_contracts¶
This marker is used to automatically parameterize a test with all system contracts that are valid for the fork being tested.
import pytest
from execution_testing.tools import Alloc, StateTestFiller
from execution_testing.base_types import Address
@pytest.mark.with_all_system_contracts
@pytest.mark.valid_from("Cancun")
def test_something_with_all_system_contracts(
state_test: StateTestFiller,
pre: Alloc,
system_contract: Address,
):
pass
In this example, the test will be parameterized for parameter system_contract with value [0x000F3DF6D732807EF1319FB7B8BB8522D0BEAC02] for fork Cancun.
Covariant Marker Keyword Arguments¶
All fork covariant markers accept the following keyword arguments:
selector¶
A lambda function that can be used to filter the fork covariant values that are valid for this specific test.
import pytest
from execution_testing.tools import Alloc, StateTestFiller
@pytest.mark.with_all_tx_types(selector=lambda tx_type: tx_type != 2)
@pytest.mark.valid_from("London")
def test_something_with_all_tx_types(
state_test: StateTestFiller,
pre: Alloc,
tx_type: int
):
pass
Ideally, the lambda function should be used to explicitly filter out values that are not compatible with the test (exclusive filter), rather than explicitly selecting values (inclusive filter), as the parametrized values might change with future forks.
marks¶
A marker, list of markers, or a lambda function that can be used to add additional markers to the test.
import pytest
@pytest.mark.with_all_tx_types(
marks=lambda tx_type: pytest.mark.skip("incompatible") if tx_type == 1 else None,
)
@pytest.mark.valid_from("London")
def test_something_with_all_tx_types_but_skip_type_1(state_test_only, tx_type):
assert tx_type != 1
...
In this example, the test will be skipped if tx_type is equal to 1 by returning a pytest.mark.skip marker, and return None otherwise.
@pytest.mark.parametrize_by_fork¶
A test can be dynamically parametrized based on the fork using the parametrize_by_fork marker.
This marker takes two positional arguments:
argnames: A list of parameter names that will be parametrized using the custom function.fn: A function that takes the fork as parameter and returns a list of values that will be used to parametrize the test at that specific fork.
And one keyword argument:
marks(optional): A marker, list of markers, or a lambda function that can be used to add additional markers to the generated tests.
The marked test function will be parametrized by the values returned by the fn function for each fork.
If the parameters that are being parametrized is only a single parameter, the return value of fn should be a list of values for that parameter.
If the parameters that are being parametrized are multiple, the return value of fn should be a list of tuples/lists, where each tuple contains the values for each parameter.
import pytest
def covariant_function(fork):
return [[1, 2], [3, 4]] if fork.name() == "Paris" else [[4, 5], [5, 6], [6, 7]]
@pytest.mark.parametrize_by_fork("test_parameter,test_parameter_2", covariant_function)
@pytest.mark.valid_from("Paris")
@pytest.mark.valid_until("Shanghai")
def test_case(state_test_only, test_parameter, test_parameter_2):
pass
In this example, the test will be parametrized with the values [1, 2] and [3, 4] for the Paris fork, with values 1 and 3 being assigned to test_parameter and 2 and 4 being assigned to test_parameter_2. For the Shanghai fork, the test will be parametrized with the values [4, 5], [5, 6], and [6, 7]. Therefore, more test cases will be generated for the Shanghai fork.
If the parameters that are being parametrized is only a single parameter, the return value of fn should be a list of values for that parameter.
If the parameters that are being parametrized are multiple, the return value of fn should be a list of tuples/lists, where each tuple contains the values for each parameter.
The function can also return a list of pytest.param objects, which allows for additional markers and test IDs to be added to the test.
Fill/Execute Markers¶
These markers are used to apply different markers to a test depending on whether it is being filled or executed.
@pytest.mark.fill¶
This marker is used to apply markers to a test when it is being filled.
import pytest
from execution_testing.tools import Alloc, StateTestFiller
@pytest.mark.fill(pytest.mark.skip(reason="Only for execution"))
def test_something(
state_test: StateTestFiller,
pre: Alloc
):
pass
In this example, the test will be skipped when it is being filled.
@pytest.mark.execute¶
This marker is used to apply markers to a test when it is being executed.
import pytest
from execution_testing.tools import Alloc, StateTestFiller
@pytest.mark.execute(pytest.mark.xfail(reason="Depends on block context"))
def test_something(
state_test: StateTestFiller,
pre: Alloc
):
pass
In this example, the test will be marked as expected to fail when it is being executed, which is particularly useful so that the test is still executed but does not fail the test run.
Other Markers¶
@pytest.mark.slow¶
This marker is used to mark tests that are slow to run. These tests are not run during CI checks, and are only run when a release is being prepared.
@pytest.mark.pre_alloc_mutable¶
This marker is used to mark tests that modify the pre-alloc in a way that would be impractical to reproduce in a real-world scenario.
Examples of this include:
- Modifying the pre-alloc to have a balance of 2^256 - 1.
- Address collisions that would require hash collisions.
- EOA accounts containing code
- EOA accounts with a hard-coded nonce
- Contracts having zero-nonce
- Deploying a contract to a hard-coded address
@pytest.mark.skip()¶
This marker can be used to skip a test.
import pytest
from execution_testing.tools import Alloc, StateTestFiller
@pytest.mark.skip(reason="Not implemented")
def test_something(state_test: StateTestFiller, pre: Alloc):
pass
@pytest.mark.xfail()¶
This marker can be used to mark a test as expected to fail.
import pytest
from execution_testing.tools import Alloc, StateTestFiller
@pytest.mark.xfail(reason="EVM binary doesn't support this opcode")
def test_something(state_test: StateTestFiller, pre: Alloc):
pass