book icon indicating copy to clipboard operation
book copied to clipboard

Clarifying what it means to "run all tests against the state resulting from your production deployment script"

Open PaulRBerg opened this issue 3 years ago • 9 comments

In the Scripts section of the Best Practices guide, it says the following:

  1. Test your scripts.

Write your deploy script and scaffold tests by running that script. Then, run all tests against the state resulting from your production deployment script. This is a great way to gain confidence in a deploy script.

What does this mean, in practice? Deploy the contracts to a testnet, then run the fork tests against the contracts previously deployed?

Deploy to Anvil, then configure the --rpc-url to be localhost?

In both cases, it seems to me that one would have to write a shell script or some sort of script in package.json (if one has a Node.js set-up).

PaulRBerg avatar Jan 08 '23 19:01 PaulRBerg

No anvil/testnet required, basically just trying to convey that you can run your deploy script in the setUp portion of your tests.

So say you have Deploy.s.sol where the public run method deploys your protocol's contracts. You can have your test contracts inherit from the deploy contract, execute it, then run all tests against that resulting state. You can see an example of this pattern here: https://github.com/ScopeLift/foundry-template/blob/34958eb169eaa5c29b05a16a7d4300c0a2ead167/test/Counter.t.sol#L8-L12

mds1 avatar Jan 09 '23 04:01 mds1

Oh, but of course 😅 Sorry, I must have had a bit of brain fog when I opened the issue.

Thanks also for sharing the example - all clear now.

I still think it would be worth it to add an explicit note about what it means to test deployment scripts in practice - I can open a PR if you agree.

PaulRBerg avatar Jan 09 '23 08:01 PaulRBerg

I still think it would be worth it to add an explicit note about what it means to test deployment scripts in practice - I can open a PR if you agree.

Right below the item cited in the title we do have these warnings about testing scripts, which is related, but definitely open to more details around the cited item too

image

mds1 avatar Jan 09 '23 17:01 mds1

I have tried to implement this in my tests but it looks like broadcasting is not compatible with pranking:

FAIL. Reason: Setup failed: You have an active prank. Broadcasting and pranks are not compatible. Disable one or the other

Is there any way to work around this limitation of the cheatcodes? Maybe selectively disable the broadcasts based upon the value of an environment variable?

If there is a solution, I think that it would be worth documenting it in the Foundry Book.

PaulRBerg avatar Jan 19 '23 10:01 PaulRBerg

That error means your script has vm.startBroadcast call but no corresponding vm.stopBroadcast call before a vm.prank in a test. (Or a floating vm.broadcast that wasn't used). Perhaps you are missing a stopBroadcast call somewhere? If not, can you provide a snippet to reproduce?

mds1 avatar Jan 19 '23 13:01 mds1

I'm pretty sure that I had a vm.stopBroadcast well before the calls to the prank cheatcodes.

I will try to set up a reproducible example.

PaulRBerg avatar Jan 20 '23 12:01 PaulRBerg

@mds1 I have tried running my deployer scripts again and I bumped into the same problem, though I have been able refactor my tests to fix this. Here are the full logs:

    ├─ [0] VM::startPrank(Admin: [0x4D1d902947361FA7c8e7B4905E877823f27331B3])
    │   └─ ← ()
    ├─ [0] VM::envOr(FOUNDRY_PROFILE, )
    │   └─ ← <env var value>
    ├─ [768] DeployComptroller::run(Admin: [0x4D1d902947361FA7c8e7B4905E877823f27331B3])
    │   ├─ [0] VM::startBroadcast(0x0000000000000000000000000000000000000000)
    │   │   └─ ← "You have an active prank. Broadcasting and pranks are not compatible. Disable one or the other"
    │   └─ ← "You have an active prank. Broadcasting and pranks are not compatible. Disable one or the other"
    └─ ← "You have an active prank. Broadcasting and pranks are not compatible. Disable one or the other"

As you can see, the issue was that I had a startPrank cheatcode before startBroadcast. So, cheats don't play nicely with broadcasts - it doesn't matter if the broadcast is stopped shortly after it is started if there is already an active prank before the start.

PaulRBerg avatar Jan 20 '23 13:01 PaulRBerg

That makes sense. Though I'm not sure I follow what you're doing, i.e. why do you have a prank before a broadcast? (I assumed you had the opposite, but yes regardless you can't have both active). The flow this is trying to convey would be something like this:

// File: Deploy.s.sol
contract Deploy is Script {
  MyContract myContract;

  function run() public {
    vm.startBroadcast();
    // All deploy stuff here.
    myContract = new MyContract();
    vm.stopBroadcast();  
  }
}

// File: MyContract.t.sol
contract MyContractTest is Test, Deploy {
  function setUp() public {
    // Run the deploy script.
    Deploy.run();
    // At this point, the deploy script + broadcast ended, so pranks are safe.
  }
}

mds1 avatar Jan 20 '23 16:01 mds1

What I was doing - that prank was a leftover from my previous test deployment flows. It just remained there. After removing it, I was able to use the scripts in my tests, too.

Sorry for the back-and-forth, and thanks for sharing that example code snippet. All clear now.

PaulRBerg avatar Jan 20 '23 20:01 PaulRBerg