If I test using Motoko is the only way to simulate different callers to spin up several canisters?

I’d call my “app” canisters from a “test” canister. Would I need to use this “test” canister to spin up others and call from/through them to simulate different users? Or is there any other way?

Thanks in advance

For most cases you can simply use different dfx identities. Whenever you generate a new identity, it will show up to the canister as a separate identity. Have a look at dfx identity new --help to figure out how to generate identities, and then switch to them using dfx identity use <identity name>.

How many different users do you need (a handful or a million)?

Look into ic-repl and write a script that tests your canister, not from a test canister.

Thank you @timo & @Severin.

I rather meant using Motoko directly though. I’ve been thinking more and more about how I should test my application. I think it has many advantages to write tests in the same language. It feels easier to compare data and it forces me to deal with pattern matching. And for testing modules, I can use the compiler directly (my hope is that I can do this for actors at some point?).

While I’m glad ic-repl exists it feels a bit limiting to me.

I only need to simulate a few users. I guess the only way to do this is to use an actor class and initialize various instances of it so that I can make it call the actual canister that I’m testing. Is that correct?

Yes, in that case you’ll have to have multiple actor instances as there is no way to switch identities in Motoko. Alternatively, you could use an architecture like I show in this post, but that may be more tedious for you to set up.

1 Like

Thank you, that looks interesting!

In that case what you can do is you basically put your entire actor in a class with a synchronous interface (no async return types) and all your methods, if they care about the calling principal, have an additional argument through which they can accept the calling principal. That’s only needed for the methods that care about the caller, of course. Then you can test class that conveniently with Motoko-only. And you can make up as many different callers as you want in your tests.

Then to deploy you wrap that inside a very “thin” actor. The actor methods map 1-to-1 to class methods. They just call the class method and return the same value, just that it’s async now. For those class methods where it’s needed they pass through msg.caller.

Sorry but I don’t completely follow.

Right now I have my “test_runner” canister from which I call my let’s say “app” canister. To simulate calling from different users I want to instantiate further actors from an actor class “test_user”. I’d then call a function from my “test_runner” on an instance of the “test_user” to trigger the desired call from that “test_user” to the “app” canister. I struggle a bit with how to write the “test_user” actor class so that I can conveniently make it call the “app” canister and return the same things.

Are you proposing something different?

You said you wanted to use Motoko directly. So what I was proposing was to not test canisters but to test Motoko classes directly with a set of unit tests. So there are no actors and no canisters in your test setup. The idea is to put 99.9% of your code in a class that you can test like that.

If your canister is going to be an actor that wraps around this class with some boiler plate code then there is not really anything left to test in that boiler plate code.

1 Like

Ahh I understand, thank you very much, that is very helpful!