Remix Solidity单元测试调用合约函数时sender未切换为指定账户问题
问题根因
你遇到的问题本质是Remix单元测试的调用模型和预期不符,核心有两个关键点:
- 你在测试函数上加的
/// #sender: account-1注解,仅作用于「外部账户调用测试合约的enterSuccessfullyMultipleEntrants方法」这一步,所以测试函数内的msg.sender确实是account1,符合你断言的结果。但你在测试函数内部调用lotto.enterDebug1时,发起调用的主体是测试合约本身的地址,所以LottoMock合约的enterDebug1方法拿到的msg.sender是测试合约地址,既不是account1也不是account0。 - 你判断调用方是account0,大概率是在LottoMock的逻辑里误用了
tx.origin来记录参与者。Remix执行单条测试用例时,会先执行beforeEach再执行测试函数,两个步骤是在同一个交易内完成的,你的beforeEach指定了调用方为account0,所以整个交易的tx.origin固定为account0,哪怕测试函数的msg.sender被设置为account1,tx.origin也不会变化,这就是你拿到account0的原因。
解决方案
如果要模拟不同外部账户调用LottoMock,不能直接在测试合约内调用LottoMock的方法,需要改用底层调用显式指定调用方:
function enterSuccessfullyMultipleEntrants() public payable { Assert.equal(lotto.getLotteryBalance(), uint256(500000000000000), "One user has already entered."); Assert.equal(lotto.getQuantityOfEntrants(), uint256(1), "Expecting an existing entry."); Assert.equal(msg.sender, TestsAccounts.getAccount(1), "Invalid sender"); // 模拟account1调用enterDebug1方法 address account1 = TestsAccounts.getAccount(1); (bool success, bytes memory returnData) = address(lotto).call{ from: account1, value: 500000000000000 }(abi.encodeWithSignature("enterDebug1()")); // 对应原来的try catch逻辑 if (success) { Assert.ok(false, 'succeed unexpected'); } else { if (returnData.length >= 4) { string memory reason = abi.decode(returnData[4:], (string)); Assert.equal(reason, "debug", "debug."); } else { Assert.ok(false, 'failed unexpected'); } } Assert.equal(lotto.getLotteryBalance(), uint256(1000000000000000), "expecting lottery balance equal to entrance fee for two users after entering"); Assert.equal(lotto.getQuantityOfEntrants(), uint256(2), "second user should have successfully entered the lottery"); }
额外建议:除非你明确需要获取交易的最初发起方,否则不要在业务逻辑里使用tx.origin,改用msg.sender即可,既符合常规业务需求,也能避免tx.origin带来的安全风险。
内容的提问来源于stack exchange,提问作者Samuel French
相关产品推荐
相关产品推荐

