-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[test] Allow test scripts to import types from deployed contracts #197
[test] Allow test scripts to import types from deployed contracts #197
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit skeptical about supporting this tbh (It looks cool to see this working nevertheless! and also great work to get it working).
The main reason is (also as you've mentioned) that users wouldn't know the address of the contract.
However, we can't avoid the chicken-and-egg problem, developers will have to probably
log the account to get the address.
This assumes blockchain.createAccount()
always creates the same account (with the same address) in each run, which might not always be the case.
One way around this could be the use of With this, the addresses for created accounts, are guaranteed to be the same in each run: import Test
import FooContract from 0x0000000000000005
pub let blockchain = Test.newEmulatorBlockchain()
pub let account = blockchain.createAccount() I have implemented this approach in this commit: 2817523 Another workaround could be to deploy contracts to import Test
import FooContract from 0xf8d6e0586b0a20c7
pub let blockchain = Test.newEmulatorBlockchain()
pub let admin = blockchain.serviceAccount()
pub fun setup() {
let contractCode = Test.readFile("../contracts/FooContract.cdc")
let err = blockchain.deployContract(
name: "FooContract",
code: contractCode,
account: admin,
arguments: []
)
Test.expect(err, Test.beNil())
blockchain.useConfiguration(Test.Configuration({
"../contracts/FooContract.cdc": admin.address
}))
} What are your thoughts on these? |
Nice, using |
This will guarantee that the addresses of created accounts will be sequential and start from 0x01. Thus, allowing developers to safely import types of deployed contracts.
2817523
to
767d32b
Compare
How about: import Test
import FooContract from "../contracts/FooContract.cdc"
pub let blockchain = Test.newEmulatorBlockchain()
pub let admin = blockchain.serviceAccount()
pub fun setup() {
let err = blockchain.deployContract(
name: "FooContract",
source: "../contracts/FooContract.cdc",
account: admin,
arguments: []
)
Test.expect(err, Test.beNil())
} this way we will also have editor support ( I assume ) @SupunS knows better but probably we can have import handler ( as we track on deploy address and source location ) |
@bluesign You are correct, this would give us editor support. However, we need the address to "trick" the type checker into thinking that the types returned from blockchain scripts, are indeed imported in the test script. For example, if a script returns the let typ = Type<FooContract.NumberAdded>()
let events = blockchain.eventsOfType(typ)
let event = events[0] as! FooContract.NumberAdded
Test.assertEqual(78557, event.n)
Test.assertEqual("Sierpinski", event.trait)
This seems promising though 👌 , it didn't cross my mind that we do track both deploy address and source location. I will try to see if I can use the relative path import, and replace it with the actual deploy address in the import handler. |
yeah I changed the signature of deploy in the example for that purpose, can be some new function like: then we can inject imports after setup phase. ( I assume setup is running before ) |
Oh right 😅 Right now we are just passing the |
Yeah, I agree with @bluesign, we could try to make the developer experience even more smoother, if possible. I was also thinking it might be a good idea to merge the two environments. e.g.: onflow/cadence#1993. I have been advocating against it earlier, but you are right, and I'm also starting to feel it might be actually good. i.e: It might be OK to just have one blockchain per test suite/file, and both the test script and the 'testing codes' share that. @m-Peter Can you maybe open another issue for this? So we can discuss it further. We could also propose this change as a FLIP if needed. But might have to do some proof-of-concept on what's possible and how would it work afterward, before we propose. |
@SupunS & @bluesign Unfortunately this is hard to overcome. The reason is that imports from import FooContract from "../contracts/FooContract.cdc"
pub let fooContract = FooContract() If I try to cast a contract nested type, returned from a script executed against a blockchain, I get the following error: error: failed to force-cast value: expected type `[S.../contracts/FooContract.cdc.FooContract.SpecialNumber]`, got `[A.0000000000000005.FooContract.SpecialNumber]`
The two environments here being the script environment and the one from
Sure thing, I'll create another issue for this 👍 |
How currently tests are run ? When file is loaded there is no FooContract at 0xf8d6e0586b0a20c7 no ? Is setup somehow running before hand? import Test
import FooContract from 0xf8d6e0586b0a20c7
pub let blockchain = Test.newEmulatorBlockchain()
pub let admin = blockchain.serviceAccount()
pub fun setup() {
let contractCode = Test.readFile("../contracts/FooContract.cdc")
let err = blockchain.deployContract(
name: "FooContract",
code: contractCode,
account: admin,
arguments: []
)
Test.expect(err, Test.beNil())
blockchain.useConfiguration(Test.Configuration({
"../contracts/FooContract.cdc": admin.address
}))
} |
@bluesign You are right, there is no |
@m-Peter all clear now, thanks a lot. I think @SupunS suggestion of merging environments still not solving the issue though. As we cannot import before we deploy. I think the easiest way would be, swizzling the declaration on deploy contract. |
That's exactly right @bluesign . I have mentioned this problem in the description: // However, we can't avoid the chicken-and-egg problem, developers will have to probably
// log the account to get the address, which will be sequential and guaranteed to be the same.
// import FooContract from account.address is not possible. |
Yes, correct, merging two won't solve it. Even if we merge the two, it would need to construct the contract value later, at the time of the However, by merging, we might be able to remove some of the pain points for developers, e.g: there would not be a blockchain Currently unit tests: import FooContract from "../contracts/FooContract.cdc"
var foo = FooContract() // construct value
foo.sayHello() // and then call the function But after: import FooContract from "../contracts/FooContract.cdc"
FooContract.sayHello() // FooContract is already the instance, just like in integration tests. So can directly call the function So it is unified for both integration tests and unit tests. We would also be able to get rid of setting the configurations for the blockchain. i.e: blockchain.useConfiguration(Test.Configuration({
"../contracts/FooContract.cdc": admin.address
})) Instead can make use of the |
yeah I just looked at this from integration tests perspective, but I agree unit tests would be much better if we merge. |
Description
Below is a working example of utilizing types from deployed contracts, inside of a test script:
master
branchFiles changed
in the Github PR explorer