You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 11_unit_testing.md
+77-46Lines changed: 77 additions & 46 deletions
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,7 @@
1
1
# Unit Testing
2
2
3
-
The typical way to add unit tests to our program is to first add the `#[cfg(test)]` annotation and then place our test code under a separate module, as identified by the `mod` keyword. Here's an example:
3
+
The typical way to add unit tests to our program is to first add the `#[cfg(test)]` annotation and then place our test code under a separate module, as identified by the `mod` keyword.
4
+
Here's an example:
4
5
5
6
```rust
6
7
#[cfg(test)]
@@ -15,7 +16,7 @@ mod tests {
15
16
16
17
The `#[cfg(test)]` annotation on the tests module tells Rust to compile and run the test code only when you run `cargo test`, not when you run `cargo build`.
17
18
18
-
So let's start setting up our tests:
19
+
So let's start setting up our tests by adding the following after the `main` function:
19
20
```rust
20
21
#[cfg(test)]
21
22
modunit_tests {
@@ -28,11 +29,16 @@ mod unit_tests {
28
29
}
29
30
```
30
31
31
-
Notice how we call `use super::read_compact_size`. We have to bring the function we're testing into scope because we are in a separate `unit_tests` module and need access to private functions. In Rust, unless we add the `pub` keyword to a function, it is private.
32
+
Notice how we call `use super::read_compact_size`.
33
+
We have to bring the function we're testing into scope because we are in a separate `unit_tests` module and need access to private functions.
34
+
In Rust, unless we add the `pub` keyword to a function, it is private.
32
35
33
-
We identify each test function with the `#[test]` annotation. We can have other functions in the test module without that annotation. This means they won't be run as tests, but could be useful as helper functions for other tests in the module.
36
+
We identify each test function with the `#[test]` annotation.
37
+
We can have other functions in the test module without that annotation.
38
+
This means they won't be run as tests, but could be useful as helper functions for other tests in the module.
34
39
35
-
Now, if you run `cargo test` from the command line instead of `cargo run`, your tests will run and return results to the terminal. You might see something like the following:
40
+
Now, if you run `cargo test` from the command line instead of `cargo run`, your tests will run and return results to the terminal.
41
+
You might see something like the following:
36
42
37
43
```console
38
44
Finished test [unoptimized + debuginfo] target(s) in 0.01s
@@ -41,10 +47,13 @@ Now, if you run `cargo test` from the command line instead of `cargo run`, your
41
47
running 1 test
42
48
test unit_tests::test_reading_compact_size ... ok
43
49
44
-
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Pretty cool. Let's start adding some logic for testing our `read_compact_size` function. We'll start simple.
54
+
Pretty cool.
55
+
Let's start adding some logic for testing our `read_compact_size` function.
56
+
We'll start simple.
48
57
49
58
```rust
50
59
#[cfg(test)]
@@ -60,7 +69,9 @@ mod unit_tests {
60
69
}
61
70
```
62
71
63
-
If you look at the `assert_eq!` statement, we intentionally set the expected length to `2_u64` instead of the correct one, `1_u64`. This is to make sure our tests are running properly and will fail when run. We'll then make the correction and ensure the test passes.
72
+
If you look at the `assert_eq!` statement, we intentionally set the expected length to `2_u64` instead of the correct one, `1_u64`.
73
+
This is to make sure our tests are running properly and will fail when run.
74
+
We'll then make the correction and ensure the test passes.
64
75
65
76
If you run `cargo test`, you should see the test failures:
66
77
@@ -85,12 +96,17 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
85
96
failures:
86
97
unit_tests::test_reading_compact_size
87
98
88
-
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `--bin transaction_decoder_11`
91
103
```
92
104
93
-
Take a moment to get familiar with the terminal output. The area you want to pay attention to is where it compares the `left` and `right` values of the assertion statement. The two should be equal but are not. It also displays the values, `1` and `2`. We know the correct length the function should return is `1` so let's update our test to reflect that.
105
+
Take a moment to get familiar with the terminal output.
106
+
The area you want to pay attention to is where it compares the `left` and `right` values of the assertion statement.
107
+
The two should be equal but are not.
108
+
It also displays the values, `1` and `2`.
109
+
We know the correct length the function should return is `1` so let's update our test to reflect that.
94
110
95
111
```rust
96
112
#[cfg(test)]
@@ -108,7 +124,8 @@ mod unit_tests {
108
124
109
125
Now if you run `cargo test` again, you will see that all tests pass with the output line at the end, `test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s`
110
126
111
-
So far so good. Let's add another testing scenario.
127
+
So far so good.
128
+
Let's add another testing scenario.
112
129
113
130
```rust
114
131
#[cfg(test)]
@@ -128,11 +145,18 @@ mod unit_tests {
128
145
}
129
146
```
130
147
131
-
We added another scenario for when the first byte is `0xfd` or the integer `253`. In this case, we want to read the next two bytes to determine the input length. So if we pass this slice in, we should get the correct length. What is the length represented by the bytes `0` and `1`? Well we can use base math to confirm. Remember, bytes are in little endian, so the `0` is in the 0th position and the `1` is in the first position. We can ignore the 0 and the other value is `1*256_u64.pow(1)` or just `256_u64`.
148
+
We added another scenario for when the first byte is `0xfd` or the integer `253`.
149
+
In this case, we want to read the next two bytes to determine the input length.
150
+
So if we pass this slice in, we should get the correct length.
151
+
What is the length represented by the bytes `0` and `1`?
152
+
Well we can use base math to confirm.
153
+
Remember, bytes are in little endian, so the `0` is in the least significant position and the `1` is in the most significant position: `1*256_u64.pow(1) + 0*256_u64.pow(0)` or just `256_u64`.
132
154
133
-
You also might have noticed that we declared the same variables, `bytes` and `length`. This is called [*shadowing*](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing) in Rust as the second declaration will "*overshadow*" the first. It's easier to write it this way sometimes, rather than come up with new variable names for each scenario.
155
+
You also might have noticed that we declared the same variables, `bytes` and `length`.
156
+
This is called [*shadowing*](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing) in Rust as the second declaration will "*overshadow*" the first.
157
+
It's easier to write it this way sometimes, rather than come up with new variable names for each scenario.
134
158
135
-
Let's add a few more scenarios testing the other arms of the match statement:
159
+
Let's add a few more scenarios to test the other arms of the match statement:
136
160
137
161
```rust
138
162
#[cfg(test)]
@@ -160,49 +184,56 @@ mod unit_tests {
160
184
}
161
185
```
162
186
163
-
Let's add another scenario from a real world example. We're going to use an example that was mentioned in the [Learn Me A Bitcoin](https://learnmeabitcoin.com/explorer/tx/52539a56b1eb890504b775171923430f0355eb836a57134ba598170a2f8980c1) tutorial site. It's a [transaction](https://mempool.space/tx/52539a56b1eb890504b775171923430f0355eb836a57134ba598170a2f8980c1) with 20,000 inputs which was confirmed in 2015. 840,000 vbytes large and paid 0 fees! It was all 0 inputs and 0 output as well so no amount of Bitcoin was transferred. Interesting.
187
+
Let's add another scenario from a real world example.
188
+
We're going to use an example that was mentioned in the [Learn Me A Bitcoin](https://learnmeabitcoin.com/explorer/tx/52539a56b1eb890504b775171923430f0355eb836a57134ba598170a2f8980c1) tutorial site.
189
+
It's a [transaction](https://mempool.space/tx/52539a56b1eb890504b775171923430f0355eb836a57134ba598170a2f8980c1) with 20,000 inputs which was confirmed in 2015.
190
+
It is 840,000 vbytes large and paid 0 fees!
191
+
It was all 0 inputs and 0 output as well so no amount of Bitcoin was transferred.
192
+
Interesting.
164
193
165
194
Here is the raw transaction hex: https://mempool.space/api/tx/52539a56b1eb890504b775171923430f0355eb836a57134ba598170a2f8980c1/hex
166
195
167
-
If we look at the first few bytes, we can see the version followed by `fd`.
196
+
If we look at the first few bytes, we can see the version followed by `fd`: `01000000fd204e`
168
197
169
-
`01000000fd204e`
170
-
171
-
`fd` indicates that the input length comes from the next two bytes. So the bytes, `0x20` and `0x4e` should evaluate to 20,000. Let's confirm this in our test.
198
+
`fd` indicates that the input length comes from the next two bytes.
199
+
So the bytes, `0x20` and `0x4e` should evaluate to 20,000.
Run this with `cargo test` and all the tests should pass!
204
233
205
-
Great! We've learned about unit testing. We'll keep this in mind as we write more functions with complex logic. Let's keep it moving and keep reading the transaction.
234
+
Great! We've learned about unit testing.
235
+
We'll keep this in mind as we write more functions with complex logic.
236
+
Let's keep it moving and keep reading the transaction.
206
237
207
238
### Additional Reading
208
239
* Test Organization: https://doc.rust-lang.org/book/ch11-03-test-organization.html
0 commit comments