1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//! This crate is an implementation of for Scrypto-Test, a unit testing framework for Scrypto that
//! follows an invocation-based approach instead of a transaction-based approach to testing,
//! allowing Scrypto developers to write tests that look and feel like Scrypto. Scrypto-Test is not
//! a replacement for transaction-based tests offered by the LedgerSimulator, it would just be an
//! addition and another way for Scrypto developers to test their code where Scrypto-Test could be
//! classified as a unit-testing framework while the LedgerSimulator could be classified as an
//! integration-testing framework.
//!
//! # Why
//!
//! We already have a way to test Scrypto blueprints in the from of the scrypto_test::prelude::LedgerSimulator
//! which is essentially an in-memory ledger that we can run transactions against, get back
//! transaction receipts, and determine-based on the TransactionReceipt-if the behavior of the
//! blueprint or component is as we expect or not. This approach is tried and tested and has been
//! proven to work as evident by the hundreds and thousands of tests in the radix_engine_tests crate
//! that make it clear that the transaction-based approach works. However, it has a number of
//! issues, especially when we think about the target audience of our testing framework: DeFi
//! developers.
//!
//! In the current (transaction-based) model, there is a lot of boilerplate code involved to write
//! what should be a straightforward test. As an example, to test that a contribution of X and Y
//! resources to Radiswap results in Z pool units minted the test author needs to:
//!
//! * Create these two resources to use for testing.
//! * Decide on whether these resources should just be mintable on demand or if the supply of these
//! resources should be stored in some account that will be used in the test to withdraw from.
//! * If an account will hold those resources, then the author needs to create that account.
//! * Split out the instructions into multiple manifests as needed such as in cases where one
//! instruction depends on the output of a previous instruction.
//! * Ensure that the execution of the previous transactions did indeed succeed and extract out the
//! information required from receipts either through the worktop changes, balance changes, or by
//! other means.
//! * Manage and ensure that the worktop by the end of the transaction is empty of all resources and
//! that the accounts that the resources will be deposited into sign the transaction.
//!
//! Most if not all of the items listed above are not core to what the developer wishes to test,
//! recall that they wished to test whether a contribution of X and Y resources returns Z pool
//! units. However, they spent a majority of their time thinking about completely different
//! problems. Thus, there is not only a large amount of boilerplate code, but there is also large
//! mental overhead for a test should be conceptually easy and simple to write. As you can see from
//! the description above, most of the time that is spent writing tests is **not spent writing
//! tests**, but spend initializing and creating the environment and managing side effects just to
//! then write a simple test in the form of a method call to some node id.
//!
//! # Scrypto-Test Model
//!
//! This model differs from the transaction based model of writing tests in that we do not have
//! transaction instructions, processor, and worktop at all. In fact, **nothing** related to
//! transactions exists in this model. Instead, there exists a [`TestEnvironment`] struct that each
//! test can instantiate instances of. Each [`TestEnvironment`] instance has a substate store,
//! track, and kernel. On top of that, [`TestEnvironment`] implements the [`SystemApi`] trait. The
//! [`TestEnvironment`] can be looked at as self-contained Radix Engine that’s exposed through the
//! [`SystemApi`] and that has some other high-level helper methods as it contains all of the layers
//! of the engine. This means that:
//!
//! * A [`TestEnvironment`] instance is a self-contained instance of the Radix Engine and Kernel
//! which are exposed through the [`SystemApi`].
//! * Since [`TestEnvironment`] implements the [`SystemApi`] it can be used as a substitute to
//! ScryptoEnv from Scrypto and the SystemService from native. This means that the simple
//! interface seen in the radix_native_sdk crate can be used within tests.
//! * If each test has it’s own [`TestEnvironment`] instance (they instantiate that themselves if
//! they need it), then tests have no shared dependencies and are isolated.
//! * The biggest struggle with the transaction-based model was around dealing with transient nodes.
//! More specifically, if we wanted to make sure that bucket X returned from some invocation
//! contained Y resources, how would we do that? Unfortunately, there was no easy way to do it. In
//! this model, if we make an invocation and get a Bucket back, there is no worktop for the bucket
//! to go into, we have a proper Bucket object that we can call amount() on and assert against.
//! Thus, this approach makes it easier to have assertions around transient nodes.
//!
//! Once the [`TestEnvironment`] has been instantiated we would get a Kernel with two Call Frames:
//! 1. **The Root Call Frame:** We have a root Call Frame to be consistent with how other parts of
//! the stack use the kernel where there is always a root Call Frame. After the instantiation is
//! complete, the root callframe is pushed onto the stack of previous Call Frames the kernel has.
//! 2. **The Test Call Frame:** This is the Call Frame that is used for all of the invocation that
//! will be made throughout the test. This Call Frame functions exactly like any other Call
//! Frame, it can own nodes, get messages from other Call Frames, and so on. As an example, say
//! we invoke a method on some node that returns a Bucket, this Bucket is now owned and visible
//! to this Call Frame. We are now able to call methods such as resource_address() and amount()
//! on this Bucket since it’s a node we own and the [`TestEnvironment`] has a Heap.
//!
//! [`SystemApi`]: crate::prelude::SystemApi
//! [`TestEnvironment`]: crate::prelude::TestEnvironment
/// Includes the WASM file of a Scrypto package.
///
/// Notes:
/// * This macro will NOT compile the package;
/// * The binary name is normally the package name with `-` replaced with `_`.
///
/// # Example
/// ```ignore
/// # // Ignoring because of include_code!
/// use scrypto::prelude::*;
///
/// // This package
/// let wasm1 = include_code!("bin_name");
///
/// // Another package
/// let wasm2 = include_code!("/path/to/package", "bin_name");
/// ```
/// Includes the schema file of a Scrypto package.
///
/// Notes:
/// * This macro will NOT compile the package;
/// * The binary name is normally the package name with `-` replaced with `_`.
///
/// # Example
/// ```ignore
/// # // Including because of include_schema!(..)
/// use scrypto::prelude::*;
///
/// // This package
/// let schema1 = include_schema!("bin_name");
///
/// // Another package
/// let schema2 = include_schema!("/path/to/package", "bin_name");
/// ```