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

Framework for testing interactions with CloudClient #127

Merged
merged 5 commits into from
Oct 11, 2023

Conversation

kate-goldenring
Copy link
Collaborator

This is a proposed approach to increasing our unit tests in this repository by making the CloudClient mockable. It uses the mockall crate by having the CloudClient implement a CloudClientInterface that can be mocked. Also has a few tests for examples.

Pros to this approach:

  • Can add more tests to ensure user input is erroring as expected
  • Can add tests to ensure we are handling the data returned from the cloud

Cons:

  • As @itowlson mentions, with mocks, tests can be verbose and potentially summed up to "we can confirm it makes this series of API calls but 🤷"
  • Requires passing the client as an argument to any testable functions

(I'm sure there are other pros and cons i am forgetting)

fixes #124

Signed-off-by: Kate Goldenring <kate.goldenring@fermyon.com>
Signed-off-by: Kate Goldenring <kate.goldenring@fermyon.com>
Signed-off-by: Kate Goldenring <kate.goldenring@fermyon.com>
@kate-goldenring
Copy link
Collaborator Author

@itowlson @rylev I'd love your thoughts on this in general is low priority

Copy link
Contributor

@rylev rylev left a comment

Choose a reason for hiding this comment

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

Thanks for working on this! I think the direction looks good. I've not worked with the mockall crate before, but it looks nice.

src/commands/link.rs Outdated Show resolved Hide resolved
Ok(_) => panic!("Expected error"),
Err(err) => assert_eq!(
err.to_string(),
r#"Database "does-not-exist" does not exist"#
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a little worried about doing exact matches on error messages as this can be pretty brittle, but I think it's fine to leave for now and see how it works out in practice.

src/commands/link.rs Outdated Show resolved Hide resolved
src/commands/link.rs Outdated Show resolved Hide resolved
src/commands/link.rs Outdated Show resolved Hide resolved
src/commands/sqlite.rs Outdated Show resolved Hide resolved
@kate-goldenring kate-goldenring marked this pull request as ready for review October 9, 2023 16:41
…all counts

Signed-off-by: Kate Goldenring <kate.goldenring@fermyon.com>
Copy link
Contributor

@itowlson itowlson left a comment

Choose a reason for hiding this comment

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

This is great to see. I left a few comments but nothing showstopping.

I did, as expected, find the mock-API style of testing a bit tricky. The lack of asserts at the end of many of the tests threw me - but of course the assertion is actually in setting up the expectation! I think this is part of why I prefer fakes, so that I can make explicit assertions about before and after state rather than setting expectations about specific actions. However setting up fakes for something like this would be a much greater amount of work, and this seems like a pragmatic way to get some unit testing going. Thanks for driving it!

cloud-openapi = { workspace = true }
mime_guess = { version = "2.0" }
mockall = "0.11.4"
Copy link
Contributor

Choose a reason for hiding this comment

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

The other reference to this is a dev dependency. Does the cloud crate need it at runtime?

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 does because it is in a separate crate. I put it behind a feature flag now

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm... you should be able to put this behind a dev-dependency instead of a feature. Are you running into issues with that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah. I cannot seem to figure out how to put it behind a dev dependency and then use it in a testing scope from another crate that imports the crate. Can you import types that are behind a dev dependency from another crate?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Seems i am running into rust-lang/cargo#8379

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@rylev I am merging this but happy to do a follow up pr if you have ideas about how to put it behind a dev dep

@@ -1 +1,4 @@
pub mod client;
mod client_interface;
pub use client_interface::CloudClientInterface;
pub use client_interface::MockCloudClientInterface;
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess that answers that question! #[cfg(test)] maybe?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

looks like code compiled with cfg(test) is not accessible to external crates, so I wasn't able to reference the mock type after putting it behind this test flag. But I was able to put it behind a feature flag and ill push up a change for that

src/commands/sqlite.rs Outdated Show resolved Hide resolved
src/commands/link.rs Show resolved Hide resolved
}

#[tokio::test]
async fn test_sqlite_link_success() -> Result<()> {
Copy link
Contributor

Choose a reason for hiding this comment

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

To me, what this test tests is not "success" but something more like "if the database exists, the link is created". It would be great to express this more explicitly. I'm not sure if it can be done in the test name - it might be too long! - but if not then maybe a comment?

Similarly with many of the other tests e.g. "if the label already points to the requested database, it errors" or "if the label already points to a different database, it..."

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 made the sqlite test names more explicit and readable. Let me know what you think.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks! The link test names are definitely more expressive!

src/commands/sqlite.rs Show resolved Hide resolved
Copy link
Contributor

@itowlson itowlson left a comment

Choose a reason for hiding this comment

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

I suggested some names for the create/delete tests - feel free to use or biff them as you prefer! LGTM regardless - thanks!

src/commands/sqlite.rs Outdated Show resolved Hide resolved
src/commands/sqlite.rs Outdated Show resolved Hide resolved
src/commands/sqlite.rs Outdated Show resolved Hide resolved
src/commands/sqlite.rs Outdated Show resolved Hide resolved
Signed-off-by: Kate Goldenring <kate.goldenring@fermyon.com>
@kate-goldenring kate-goldenring merged commit 04f3028 into fermyon:main Oct 11, 2023
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

More unit test coverage
3 participants