-
Notifications
You must be signed in to change notification settings - Fork 85
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
SDK-576 Design: Dfx testing story #354
Conversation
A meta level question: How is build different from test? To me, it's all about running a script and inspecting the return code. If we are designing a DSL, we should aim to solve both processes, and possibly depolyment in one shot. |
You are welcome to wrap The goal of |
Re: building versus testing. In the interests of reaching agreement, I want to avoid talking about building here, which seems separable in phase, but not totally independent, of course. To me, testing is about engaging with the replica and seeing what some previously-built wasm code does on it dynamically; building is about obtaining that wasm, and that happens earlier, before testing. It seems separable to me in that sense (distinct phases, in the Of course, nothing is really separate from anything in terms of "functional dependencies" (think While I think that we do want the beautiful, general solution eventually, a more modest proposal like this may help us understand what we want in detail without having to first build the most general thing we can imagine (which is a task I'd like to avoid in the short term). This proposal can be yet another midway point to that super-general paradise in the sky that we all want to reach, some day. Let's just be more modest in the short term. :) Finally, FWIW, I also have no appetite (now or ever) to adopt whatever technology Google thinks is best for its engineering culture. My general feeling is that Google's preferences probably don't translate well for us, given how different they are from us as an org, especially around building and testing code. We have small repos with small numbers of devs; they have the opposite. They over-engineer everything because they have "too many" engineers. That's not our problem. |
|
||
``` | ||
<dfx-result-pattern> ::= bind <test-script-var-id> | ||
| match <candid-value-pattern> |
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.
If we agree that match
is the form we want, we can drop the less general bind
form and have that each "dfx result pattern" is the same as a candid value pattern.
I think that makes the most sense, personally. I only included both cases to point out that the simpler (first) case is also useful on its own.
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.
Hope the feedback's helpful.
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.
Submitting a non-committal review to indicate that I'm following the conversation here but don't have strongly formed opinions yet. I guess I'd like to see some examples of where developers would put the e2e tests, how'd they'd be written, etc. I can extrapolate from this document but I'd love to see things explicitly stated.
@stanleygjones thanks for the note. Per your suggestion, I will add some concrete examples next. |
I've added an example project tree, and some candidate example syntax for the test scripts. @stanleygjones PTAL |
}; | ||
}; | ||
|
||
//SimpleAdaptonDivByZero.go(); |
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.
This is a common class of test: it's a single canister, and a single call (e.g., go()
like here.)
So to be clear, this class is so common that I don't even want to write a script for any of them. The idea is that mentioning them in dfx.json
is enough to get the test system to call their go
function (or whatever we call it).
I call each a "functional test" since each one is much more than a unit test, but not as complex as a multi-canister test, or one that requires multiple calls. Within one call, we can do a pretty full test of a non-actor library's functionality, as illustrated here.
(Thanks for the idea @nikclayton-dfinity!)
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.
Is that comment load bearing?
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 don't understand the question.
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.
Has that comment semantic meaning?
"AliceBob": { | ||
"_comment:": "test-script installs and scripts bots `alice` and `bob`" | ||
"test-script": "test/aliceBob/test-script" | ||
"aliceCanister": "test/aliceBob/aliceCanister.mo" |
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.
Are those (already defined canisters) necessary? I see the need for a test-script
key I just don't see the need to repeat canisters that are already listed in the /canisters
key.
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.
the ideas here are that
- some canisters are just for tests, and are only defined for the tests that require them, and
- separate name scoping of canister names within e2e tests and their test scripts, but not between distinct e2e tests; they are independent, by assumption. So, distinct e2e tests do not share canister names, and can redefine them to point at different files, with different implementations (I don't do that here, though).
So, the aliceCanister
, bobCanister
and charlieCanister
keys are only defined for the e2e
test that defines them. So, they have to be defined if they are reused, as they are here. The idea was to show exactly this kind of sharing, given the restricted scoping of the canister names.
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.
Are there reasons to scope them? It seems this just causes more copy-paste.
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.
Separate namespaces.
I expect that separate namespaces will generally be more useful than one big shared global one, where you have to "mimic" the separation I want here by introducing long names for everything. In terms of implementation complexity, separate namespaces is no more complex either; there's no implementation-based reason not to support them.
In terms of user value: If I have two test scripts that introduce two cansiters called testBot
, implemented in two different directories, it's the filesystem structure that distinguishes them as files; as canisters that appear in test scripts, we need to distinguish them to give them the same short name, like testBot
, and know that this same short name is never ambiguous. Separate namespaces (much like the scoping rules of any PL with namespaces/modules) is the answer.
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.
It seems this just causes more copy-paste.
I don't follow this concern.
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.
Removing some boiler-plate copy-paste; aliceCanister
seems to always point to test/aliceBob/aliceCanister.mo
, so maybe instead of doing the name => main_file map, we could just list the canisters we want to expose. Maybe if we move those test definitions to their own files so they free up dfx.json would also be better. WDYT?
==> #ok(bobId) | ||
install aliceCanister | ||
==> #ok(aliceId) | ||
call bobId addFriend aliceId |
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.
With inter-canister calls, could you elaborate on your rationale for using a DSL instead of actual Motoko?
In Motoko:
let result = await bob.addFriend(aliceId);
assert(result == Ok); // or some Motoko
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.
The issue is scoping.
The intention of this Alice canister code is that it does not import (or know about) Bob's code statically, and vice versa. They are "test bots" written independently, and only linked dynamically, by a test script.
I realize that this tiny example could mention those names using aliases; but, the point here was to illustrate a multi-canister test that does not use aliases, and still has canisters interact.
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.
Where does bobId
come from? Is it a local identifier? How is its name derived?
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.
Yes, bobId
is a test script variable, bound by the result pattern #ok(bobId)
. This pattern will not match an error/trap result, only a successful one.
In general, these patterns may have variables, which may appear free in subsequent actions, as illustrated here with bobId
.
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.
Ha so ==> #ok(abc)
is also a declaration for abc
? I need to read the rest of this document, sorry ^_^
Can we figure out what next steps to take here without having a meeting? (That's not a sarcastic question.) Since the first draft, the latest idea here introduces three classes of tests:
If we say that number 1 is a special case of number 2, and say that number 3 is for "later" (TBD), then we can focus on 2 without much more bikeshedding, I think. The latest threads of discussion between myself and @hansl above are around the exact formal semantics (and perhaps concrete syntax) for the test scripts in item 3. I say we defer resolving those discussions until we have 1 and 2 working in some capacity. I think this captures the collective sentiment of the standup today. What do others think? (cc @stanleygjones) |
In parallel with waiting for the discussion to resolve, I will make a first attempt at doing what I said just above, implement I expect that the unit testing command We can wait for the full testing story, including multi-canister e2e tests, until we a working story for simpler tests. |
Update: I looked into this, but I fear that Today, each dfx "command" makes the assumption that the caller is a shell script or human writing something against the CLI. That's reasonable in simple use cases, but it is simply not the case for To meet this need, as well as more automation needs, I think we need a more systematic architecture changes to dfx that separate functions for this behavior (in Rust) from the layer of Rust that processes strings from the CLI-using human or shell script. Ideally, the CLI/string-processing logic would be isolated into a very small part of the However, as things stand now, it seems like wasted effort to implement anything for Even so, I assume that this separation I am attempting to advocate will also be controversial. In any case, it is involved. It is more than a simple PR, and has a global scope, well beyond any one JIRA story. I assume that these facts are part of the reason we can keep avoiding this alternative course, or even discussing it earlier. Nevertheless, I'd like to discuss it before seeing |
Update: @nomeata's demo (from Global R&D) today subsumes many of the ideas from this PR and actually implements them. It targets the |
Discussion about what we mean by
dfx test
.This PR:
We focus on Motoko here, but not especially so; should work more generally.
Many open things left to nail down, especially around the color of the bike sheds. Discussion is very welcome.