Skip to content
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

Add slide to show the Mockall crate in action #1532

Merged
merged 2 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
- [Other Types of Tests](testing/other.md)
- [Useful Crates](testing/useful-crates.md)
- [GoogleTest](testing/googletest.md)
- [Mocking](testing/mocking.md)
- [Compiler lints and Clippy](testing/lints.md)
- [Exercise: Luhn Algorithm](testing/exercise.md)
- [Solution](testing/solution.md)
Expand Down
7 changes: 7 additions & 0 deletions src/testing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,16 @@ crate-type = ["staticlib"]
path = "googletest.rs"
test = true

[[example]]
name = "mockall-example"
crate-type = ["staticlib"]
path = "mockall.rs"
test = true

[[bin]]
name = "luhn"
path = "exercise.rs"

[dependencies]
googletest = "0.10.0"
mockall = "0.11.4"
13 changes: 13 additions & 0 deletions src/testing/mockall.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use std::time::Duration;

#[mockall::automock]
pub trait Pet {
fn is_hungry(&self, since_last_meal: Duration) -> bool;
}

#[test]
fn test_robot_pet() {
let mut mock_dog = MockPet::new();
mock_dog.expect_is_hungry().return_const(true);
assert_eq!(mock_dog.is_hungry(Duration::from_secs(10)), true);
}
58 changes: 58 additions & 0 deletions src/testing/mocking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
minutes: 5
---

# Mocking

If you want to do mocking, we recommend [Mockall]. You need to refactor your
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If you want to do mocking, we recommend [Mockall]. You need to refactor your
If you want to do mocking, AOSP recommends [Mockall], although there are several other crates available. You need to refactor your

..or something to that effect. I think the existing text implies "mockall is how people do test mocks in Rust", and that's not accurate.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a survey and from what I can tell, Mockall is vastly more used than any other mocking library right now:

Released Updated Dependants Downloads/month
Mockall 2019 2023 295 1M
Mockers 2016 2022 3 560
Simulacrum 2017 2018 1 6k
Pseudo 2017 2018 4 653
Double 2017 2019 1 208
Mockiato 2019 2019 1 2k
Mock-it 2018 2022 2 406

Most libraries haven't been updated in a few years and most of them have very few reverse dependencies.

Of course Mockall isn't the end all of mocking, so a new library could be made which is even better. But right it's the one we will recommend to our engineers in Android.

At the end of the day, the course here is written for Android engineers. I've tried my best to take out the Android-specific parts and make as much as it generic. I think the advise here is also generic enough for this section.

However, let us add a note on the slide (and perhaps somewhere in the beginning) to say that the material here is an opinionated guide to Rust. It tried to be fair and generic, but it is sponsored by Google, and it will recommend tools useful today to Android and Google engineers. Does that sound fair?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thanks for that analysis of the libraries! A few search results suggested a much wider range of options. Given that information, this slide makes sense.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I will add a note explaining that the recommendations on the slide is current at of now and apply directly to Android. They might also apply to others, but as you point out, there are other options out there!

code to use traits, which you can then quickly mock:

```rust,ignore
{{#include mockall.rs}}
```

[Mockall]: https://docs.rs/mockall/

<details>

- Note that *mocking is controversial*: mocks allow you to completely isolate a
test from its dependencies. The immediate result is faster and more stable
test execution. On the other hand, the mocks can be configured wrongly and
return output different from what the real dependencies would do.

If at all possible, it is recommended that you use the real dependencies. As
an example, many databases allow you to configure an in-memory backend. This
means that you get the correct behavior in your tests, plus they are fast and
will automatically clean up after themselves.

Similarly, many web frameworks allow you to start an in-process server which
binds to a random port on `localhost`. Always prefer this over mocking away
the framework since it helps you test your code in the real environment.
Comment on lines +18 to +30
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems out of context here -- if students are asking how to use mocks in Rust, they are probably more focused on the "in Rust" part and do not want to be told that they shouldn't be using mocks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was the first thing @qwandor brought up and it's what our internal guidelines say. However, the advice is not specific to Google or Android, it's a generic argument about using mocking in any language or environment.

I think it's good to note, even for non-Googlers: I was not aware of the "controversy" until reading more about it — I had been happily mocking for years in my Python work.

What could improve it further (in my opinion) would be a link to an article about the problems with mocking.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still want to keep it short, though, just in the interest of overall course length. I think this lengthy speaker note encourages a digression into the relative merits of various testing strategies, which will probably generate questions and comments from students, and suddenly we've been on this slide for 15 minutes.

I agree that an article would be great, but maybe this could be moved to "More to Explore"?

At any rate, LGTM.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still want to keep it short, though, just in the interest of overall course length.

Fair point! I was not thinking that the instructor would go through all the pros and cons — I guess I was writing this bullet point more for the people who will read the slide by themselves.

The problem of multiple personas reading the slides is tricky... as we've discussed before 😄 It just occurred to me that we could create more sections in the notes (and first rename them to "Notes"):

<details>

### Speaker

- Make sure to mention X and Y.
- Try removing `x`, note the compilation error.

### Participants

- The concept of Foo was originally introduced in C++ as Bar. See Wikipedia.
- If you want to explore more, then try ...

</details>

Basically an extension of the "More to Explore" headings we've added a few places.


- Mockall is not part of the Rust Playground, so you need to run this example in
a local environment. Use `cargo add mockall` to quickly add Mockall to an
existing Cargo project.

- Mockall has a lot more functionality. In particular, you can set up
expectations which depend on the arguments passed:

```rust,ignore
let mut mock_cat = MockPet::new();
mock_cat
.expect_is_hungry()
.with(mockall::predicate::gt(Duration::from_secs(3 * 3600)))
.return_const(true);
mock_cat
.expect_is_hungry()
.return_const(true);
assert_eq!(mock_cat.is_hungry(Duration::from_secs(1 * 3600)), false);
assert_eq!(mock_cat.is_hungry(Duration::from_secs(5 * 3600)), true);
```

- You can use `.times(n)` to limit the number of times a mock method can be
called to `n` --- the mock will automatically panic when dropped if this isn't
satisfied.
mgeisler marked this conversation as resolved.
Show resolved Hide resolved

- Mockall is available for use in AOSP.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not strictly true today, but it should be true by ~next week.


</details>
Loading