Skip to content

Commit 1cb95ea

Browse files
authored
Merge pull request #33 from edilmedeiros/edilmedeiros-chapt11
Edit chapter 11
2 parents c73d1c0 + f734373 commit 1cb95ea

File tree

1 file changed

+77
-46
lines changed

1 file changed

+77
-46
lines changed

11_unit_testing.md

Lines changed: 77 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Unit Testing
22

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:
45

56
```rust
67
#[cfg(test)]
@@ -15,7 +16,7 @@ mod tests {
1516

1617
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`.
1718

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:
1920
```rust
2021
#[cfg(test)]
2122
mod unit_tests {
@@ -28,11 +29,16 @@ mod unit_tests {
2829
}
2930
```
3031

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.
3235

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.
3439

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:
3642

3743
```console
3844
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
4147
running 1 test
4248
test unit_tests::test_reading_compact_size ... ok
4349

44-
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
50+
test result: ok.
51+
1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
4552
```
4653

47-
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.
4857

4958
```rust
5059
#[cfg(test)]
@@ -60,7 +69,9 @@ mod unit_tests {
6069
}
6170
```
6271

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.
6475

6576
If you run `cargo test`, you should see the test failures:
6677

@@ -85,12 +96,17 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
8596
failures:
8697
unit_tests::test_reading_compact_size
8798

88-
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
99+
test result: FAILED.
100+
0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
89101

90102
error: test failed, to rerun pass `--bin transaction_decoder_11`
91103
```
92104

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.
94110

95111
```rust
96112
#[cfg(test)]
@@ -108,7 +124,8 @@ mod unit_tests {
108124

109125
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`
110126

111-
So far so good. Let's add another testing scenario.
127+
So far so good.
128+
Let's add another testing scenario.
112129

113130
```rust
114131
#[cfg(test)]
@@ -128,11 +145,18 @@ mod unit_tests {
128145
}
129146
```
130147

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`.
132154

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.
134158

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:
136160

137161
```rust
138162
#[cfg(test)]
@@ -160,49 +184,56 @@ mod unit_tests {
160184
}
161185
```
162186

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.
164193

165194
Here is the raw transaction hex: https://mempool.space/api/tx/52539a56b1eb890504b775171923430f0355eb836a57134ba598170a2f8980c1/hex
166195

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`
168197

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.
200+
Let's confirm this in our test.
172201

173202
```rust
174203
fn test_reading_compact_size() {
175-
let mut bytes = [1_u8].as_slice();
176-
let length = read_compact_size(&mut bytes);
177-
assert_eq!(length, 1_u64);
178-
179-
let mut bytes = [253_u8, 0, 1].as_slice();
180-
let length = read_compact_size(&mut bytes);
181-
assert_eq!(length, 256_u64);
182-
183-
let mut bytes = [254_u8, 0, 0, 0, 1].as_slice();
184-
let length = read_compact_size(&mut bytes);
185-
assert_eq!(length, 256_u64.pow(3));
186-
187-
let mut bytes = [255_u8, 0, 0, 0, 0, 0, 0, 0, 1].as_slice();
188-
let length = read_compact_size(&mut bytes);
189-
assert_eq!(length, 256_u64.pow(7));
190-
191-
// https://mempool.space/tx/52539a56b1eb890504b775171923430f0355eb836a57134ba598170a2f8980c1
192-
// fd is 253
193-
// transaction has 20,000 empty inputs
194-
let hex = "fd204e";
195-
let decoded = hex::decode(hex).unwrap();
196-
let mut bytes = decoded.as_slice();
197-
let length = read_compact_size(&mut bytes);
198-
let expected_length = 20_000_u64;
199-
assert_eq!(length, expected_length);
200-
}
204+
let mut bytes = [1_u8].as_slice();
205+
let length = read_compact_size(&mut bytes);
206+
assert_eq!(length, 1_u64);
207+
208+
let mut bytes = [253_u8, 0, 1].as_slice();
209+
let length = read_compact_size(&mut bytes);
210+
assert_eq!(length, 256_u64);
211+
212+
let mut bytes = [254_u8, 0, 0, 0, 1].as_slice();
213+
let length = read_compact_size(&mut bytes);
214+
assert_eq!(length, 256_u64.pow(3));
215+
216+
let mut bytes = [255_u8, 0, 0, 0, 0, 0, 0, 0, 1].as_slice();
217+
let length = read_compact_size(&mut bytes);
218+
assert_eq!(length, 256_u64.pow(7));
219+
220+
// https://mempool.space/tx/52539a56b1eb890504b775171923430f0355eb836a57134ba598170a2f8980c1
221+
// fd is 253
222+
// transaction has 20,000 empty inputs
223+
let hex = "fd204e";
224+
let decoded = hex::decode(hex).unwrap();
225+
let mut bytes = decoded.as_slice();
226+
let length = read_compact_size(&mut bytes);
227+
let expected_length = 20_000_u64;
228+
assert_eq!(length, expected_length);
229+
}
201230
```
202231

203232
Run this with `cargo test` and all the tests should pass!
204233

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.
206237

207238
### Additional Reading
208239
* Test Organization: https://doc.rust-lang.org/book/ch11-03-test-organization.html

0 commit comments

Comments
 (0)