Improvements to state machines
See original GitHub issueOverview
Improve the design of Brownie state machines.
This is a work in progress, if you have any ideas feel free to leave them as comments or reach out on Gitter.
Specification
- Eliminate the need for an
__init__
method. Handling this as a classmethod is confusing and unintuitive. Replace it with an optionalsetup_first
classmethod that achieves the same functionality (runs once prior to the snapshot). - Eliminate the need for a test that launches the state machine. Convert the
state_machine
fixture to a decorator which accepts pytest fixtures to be made available in the test. - Along with injecting strategies as arguments for rules and invariants, also inject pytest fixtures.
Example
This is a condensed version of the example state machine given in the documentation:
class OldStateMachine:
value = strategy('uint256', max_value="1 ether")
address = strategy('address')
def __init__(cls, accounts, Depositer):
# deploy the contract at the start of the test
cls.accounts = accounts
cls.contract = Depositer.deploy({'from': accounts[0]})
def setup(self):
# zero the deposit amounts at the start of each test run
self.deposits = {i: 0 for i in self.accounts}
def rule_deposit(self, address, value):
# make a deposit and adjust the local record
self.contract.deposit_for(address, {'from': self.accounts[0], 'value': value})
self.deposits[address] += value
def invariant(self):
# compare the contract deposit amounts with the local record
for address, amount in self.deposits.items():
assert self.contract.deposited(address) == amount
def test_stateful(Depositer, accounts, state_machine):
state_machine(OldStateMachine, accounts, Depositer)
Here is the same state machine, rewritten with the proposed new functionality:
@state_machine(Depositor, accounts)
class NewStateMachine:
value = strategy('uint256', max_value="1 ether")
address = strategy('address')
def setup_first(cls, accounts, Depositer):
# deploy the contract at the start of the test
cls.contract = Depositer.deploy({'from': accounts[0]})
def setup(self, accounts):
# zero the deposit amounts at the start of each test run
self.deposits = {i: 0 for i in accounts}
def rule_deposit(self, accounts, address, value):
# make a deposit and adjust the local record
self.contract.deposit_for(address, {'from': accounts[0], 'value': value})
self.deposits[address] += value
def invariant(self):
# compare the contract deposit amounts with the local record
for address, amount in self.deposits.items():
assert self.contract.deposited(address) == amount
Dependencies
Breaking change. Can happen as a part of v2.0.0
. Also, this means plenty of time to bikeshed about the design 😄
Issue Analytics
- State:
- Created 3 years ago
- Comments:15 (14 by maintainers)
Top Results From Across the Web
The Rise Of The State Machines - Smashing Magazine
The UI development became difficult in the last couple of years. That is because we pushed the state management to the browser.
Read more >Introduction to State Machine and Use Cases
State machines model explicit states to control the transition from one value to another in response to inputs.
Read more >Why Developers Never Use State Machines - Workflow Engine
At first sight, state machines seem to be an easy tool for developers. Nonetheless, developers soon start to regret using them. Read to...
Read more >Design state machines for microservices | Red Hat Developer
State machines help engineers visualize a resource's behavior, find gaps or inconsistencies, organize code, and improve test coverage. They also ...
Read more >Using state machines to define behaviors and state ... - Medium
How do we ensure that we've included all changes? If so how do we program it? What's a state machine? A methodology for...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Related - it should be possible to selectively disable coverage analysis during a stateful test. Either on a per rule/invariant basis, or a broad “disable during invariants”.
What about:
create_snapshot
setup_case
teardown_case
revert_snapshot
Pros:
snapshot
s (happens once) vs.case
s (happensN
times)