How do I test private methods in Rust? I didn't find any information about it. There's no information in the documentation either.
When using #[test], there’s nothing special about private or public methods—you’re just writing perfectly normal functions that can access anything they can access.
fn private_function() {
}
#[test]
fn test_private_function() {
private_function()
}
External tests, such as tests/*.rs and examples/*.rs if you’re using Cargo, or doc tests, do not get access to private members; nor should they: such tests are designed to be public API tests, not to be dealing with implementation details.
I don't know if this issue is still open for you, but I found some documentation about it :
Test Organization
What I retained from it is that you can test private method but only if the test can see it (i.e. they are in the same scope), since tests follow the visibility rules as any other function.
Here is a working example :
pub fn add_two(a: i32) -> i32 {
internal_adder(a, 2)
}
fn internal_adder(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn internal() {
assert_eq!(4, internal_adder(2, 2));
}
}
All in all, please remember that the debate wether private methods should or should not be tested is still open within the test community. Both sides have valid argument and the correct answer only relies on you, your vision on testing procedure and the context of your project.
Related
Coming from Go there are a lot of interfaces you can use to do something like the below:
async fn get_servers(client: &dyn std::marker::Send) -> Result<String, impl std::error::Error> {
let servers_str = client.send().await?.text()
let v: Value = serde_json::from_str(servers_str)?;
println!("{:?}", v);
Ok(servers_str.to_string())
}
// ...
get_servers(client.get(url))
I could pass in something that just implemented the send and return the text. That way makes the code testable. I thought maybe the send auto trait would do that but apparently not. Says send not found. Maybe some kind of impl requestbuilder?
In general, this is absolutely possible and (correct me if I'm wrong) even advised. It's a programming paradigm called dependency injection.
Simplified, this means in your case, pass in the dependent object via an interface (or in Rust: trait) so you can replace it at test time with an object of a different type.
Your mistake here is that the std::marker::Send trait does not what you think it does; it marks objects for being transferrable between threads. It's closely linked to std::marker::Sync, meaning, it can be accessed by multiple threads without causing race conditions.
While many libraries already have traits you can use for that purpose, in a lot of cases you will have to set up your own trait. Here, for example, we have a hello world function, that gets tested by replacing its printer with a different one, specialized for testing. We achieve that by passing the printer into the hello world function through the abstraction of a trait, as already mentioned.
trait HelloWorldPrinter {
fn print_text(&mut self, msg: &str);
}
struct ConsolePrinter;
impl HelloWorldPrinter for ConsolePrinter {
fn print_text(&mut self, msg: &str) {
println!("{}", msg);
}
}
// This is the function we want to test.
// Note that we are using a trait here so we can replace the actual
// printer with a test mock when testing.
fn print_hello_world(printer: &mut impl HelloWorldPrinter) {
printer.print_text("Hello world!");
}
fn main() {
let mut printer = ConsolePrinter;
print_hello_world(&mut printer);
}
#[cfg(test)]
mod tests {
use super::*;
struct TestPrinter {
messages: Vec<String>,
}
impl TestPrinter {
fn new() -> Self {
Self { messages: vec![] }
}
}
impl HelloWorldPrinter for TestPrinter {
fn print_text(&mut self, msg: &str) {
self.messages.push(msg.to_string());
}
}
#[test]
fn prints_hello_world() {
let mut printer = TestPrinter::new();
print_hello_world(&mut printer);
assert_eq!(printer.messages, ["Hello world!"]);
}
}
When doing cargo run:
Hello world!
When doing cargo test:
Running unittests src/main.rs
running 1 test
test tests::prints_hello_world ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
As a little explanation, if that code doesn't explain itself:
we create a trait HelloWorldPrinter whic his the only thing our print_hello_world() function knows about.
we define a ConsolePrinter struct that we use at runtime to print the message. The ConsolePrinter of course has to implement HelloWorldPrinter to be usable with the print_hello_world() function.
for testing, we write the TestPrinter struct that we use instead of the ConsolePrinter. Instead of printing, it stores what it received so we can test whether it got passed the correct message. Of course, the ConsolePrinter also has to implement the HelloWorldPrinter trait to be usable with print_hello_world().
I hope that goes into the direction of your question. If you have any questions, feel free to discuss further.
I can't directly tell you what you should write to solve your problem, as your question is quite vague, but this should be the toolset you need to solve your problem. I hope.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I'm trying to increase the test coverage in my Rust application. I have read lots about testing public functions and testing private functions and adding the "tests" directory in order to add integration tests. But I have not read anything about testing of methods within an implementation. I've tried googling a bit for this but I'm not finding anything.
Here is a simple example, is this how I am meant to implement the testing?
struct Rectangle {
width: usize,
length: usize,
}
impl Rectangle {
pub fn new(width: usize, length: usize) -> Rectangle {
Rectangle {
width,
length,
}
}
fn area(&mut self) -> usize {
self.width * self.length
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rectangle() {
let mut rectangle = Rectangle::new(4, 5);
assert_eq!(20, rectangle.area())
}
}
Yes, that is exactly how you are meant to test struct methods. The Rust book has a chapter called Test Organization which states:
The purpose of unit tests is to test each unit of code in isolation from the rest of the code to quickly pinpoint where code is and isn’t working as expected. You’ll put unit tests in the src directory in each file with the code that they’re testing. The convention is to create a module named tests in each file to contain the test functions and to annotate the module with cfg(test).
Another common test organization methods is using documentation tests. rustdoc supports executing your documentation examples as tests. So running cargo test on the example below would cause the area function to execute as a test:
impl Rectangle {
/// ```rust
/// use crate::Rectangle;
///
/// let mut rectangle = Rectangle::new(4, 5);
/// assert_eq!(20, rectangle.area())
/// ```
fn area(&mut self) -> usize {
self.width * self.length
}
}
I'm writing a function that could return several one of several different errors.
fn foo(...) -> Result<..., MyError> {}
I'll probably need to define my own error type to represent such errors. I'm presuming it would be an enum of possible errors, with some of the enum variants having diagnostic data attached to them:
enum MyError {
GizmoError,
WidgetNotFoundError(widget_name: String)
}
Is that the most idiomatic way to go about it? And how do I implement the Error trait?
You implement Error exactly like you would any other trait; there's nothing extremely special about it:
pub trait Error: Debug + Display {
fn description(&self) -> &str { /* ... */ }
fn cause(&self) -> Option<&Error> { /* ... */ }
fn source(&self) -> Option<&(Error + 'static)> { /* ... */ }
}
description, cause, and source all have default implementations1, and your type must also implement Debug and Display, as they are supertraits.
use std::{error::Error, fmt};
#[derive(Debug)]
struct Thing;
impl Error for Thing {}
impl fmt::Display for Thing {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Oh no, something bad went down")
}
}
Of course, what Thing contains, and thus the implementations of the methods, is highly dependent on what kind of errors you wish to have. Perhaps you want to include a filename in there, or maybe an integer of some kind. Perhaps you want to have an enum instead of a struct to represent multiple types of errors.
If you end up wrapping existing errors, then I'd recommend implementing From to convert between those errors and your error. That allows you to use try! and ? and have a pretty ergonomic solution.
Is that the most idiomatic way to go about it?
Idiomatically, I'd say that a library will have a small (maybe 1-3) number of primary error types that are exposed. These are likely to be enumerations of other error types. This allows consumers of your crate to not deal with an explosion of types. Of course, this depends on your API and whether it makes sense to lump some errors together or not.
Another thing to note is that when you choose to embed data in the error, that can have wide-reaching consequences. For example, the standard library doesn't include a filename in file-related errors. Doing so would add overhead to every file error. The caller of the method usually has the relevant context and can decide if that context needs to be added to the error or not.
I'd recommend doing this by hand a few times to see how all the pieces go together. Once you have that, you will grow tired of doing it manually. Then you can check out crates which provide macros to reduce the boilerplate:
error-chain
failure
quick-error
Anyhow
SNAFU
My preferred library is SNAFU (because I wrote it), so here's an example of using that with your original error type:
use snafu::prelude::*; // 0.7.0
#[derive(Debug, Snafu)]
enum MyError {
#[snafu(display("Refrob the Gizmo"))]
Gizmo,
#[snafu(display("The widget '{widget_name}' could not be found"))]
WidgetNotFound { widget_name: String },
}
fn foo() -> Result<(), MyError> {
WidgetNotFoundSnafu {
widget_name: "Quux",
}
.fail()
}
fn main() {
if let Err(e) = foo() {
println!("{}", e);
// The widget 'Quux' could not be found
}
}
Note I've removed the redundant Error suffix on each enum variant. It's also common to just call the type Error and allow the consumer to prefix the type (mycrate::Error) or rename it on import (use mycrate::Error as FooError).
1 Before RFC 2504 was implemented, description was a required method.
The crate custom_error allows the definition of custom error types with less boilerplate than what was proposed above:
custom_error!{MyError
Io{source: io::Error} = "input/output error",
WidgetNotFoundError{name: String} = "could not find widget '{name}'",
GizmoError = "A gizmo error occurred!"
}
Disclaimer: I am the author of this crate.
Is that the most idiomatic way to go about it? And how do I implement the Error trait?
It's a common way, yes. "idiomatic" depends on how strongly typed you want your errors to be, and how you want this to interoperate with other things.
And how do I implement the Error trait?
Strictly speaking, you don't need to here. You might for interoperability with other things that require Error, but since you've defined your return type as this enum directly, your code should work without it.
Take the following code snippet:
fn main() -> std::result::Result<(), std::io::Error> {
println!("Bonjour le Monde");
Ok(())
}
This is perfectly fine code, but I wondered what would happen if I changed the type of the error to std::error::Error:
fn main() -> std::result::Result<(), std::error::Error> {
println!("Bonjour le Monde");
Ok(())
}
This is not good anymore:
error[E0277]: the size for values of type (dyn std::error::Error + 'static) cannot be known at compilation time
I fixed it like this:
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
println!("Bonjour le Monde");
Ok(())
}
So why does the main function require Box<> for standard errors, but not for io errors?
std::error::Error is a trait included in the standard library. As Rust points out, it cannot determine how much memory will be need to store an Error at compiled time since a structure of any size could potentially be used. As such, you need to use some form of indirection, such as a reference, a pointer, or, in this case, a Box. See TRPL Ch.17 for more information about using trait objects.
In contrast, std::io::Error is a structure from the std::io module (which also happens to implement std::error::Error). Rust does know large this structure is, so it can create a monomorphization of Result that uses it.
I am currently trying to write a custom error type for my CLI application. Now I want to write an implementation of the From trait so my custom error type can wrap all third party library errors that can occur.
The error enum:
#[derive(Debug)] // Allow the use of "{:?}" format specifier
pub enum CustomError {
Git(git2::Error),
Other
}
Now I want to implement the From Trait for the git2::Error from the git2 library to use the ? operator in my functions.
impl From<(git2::Error)> for CustomError {
fn from(cause: git2::Error) -> Self {
CustomError::Git(cause)
}
}
But when I try to use my custom error to map an error like this:
let repo = Repository::open(path).map_err(|err| CustomError::Git)?;
I am getting the following error message:
the trait `std::convert::From<fn(git2::error::Error) -> error::CustomError {error::CustomError::Git}>` is not implemented for `error::CustomError `
Can anyone help me to understand why I am getting this error and how to solve this problem ?
Any help is appreciated
You've mixed up a whole bunch of concepts; let's see if we can walk through this together and hopefully clarify all of it.
The git2 crate has its own error type, that you no doubt have discovered. Your definition of custom errors is fine as well.
The issue is twofold:
Your implementation of From<_>
From<E> allows you to transform a type from one type to another by providing the translation function (from()).
Your implementation of this was the following:
impl From<(git2::Error)> for CustomError {
fn from(cause: git2::Error) -> Self {
CustomError::Git(cause)
}
}
Brackets in rust aren't added where they should not be, and this is precisely one of the cases where this is the case. By doing this, you've actually defined From<(T)>, not From<T>. That's mistake #1.
The correct implementation simply drops the brackets:
impl From<git2::Error> for CustomError {
fn from(cause) -> Self {
CustomError::Git(cause)
}
}
Your actual conversion
Not an error per se, but a completely unnecessary operation as the ? operator handles it for you. There is no need for the map_err(), and if there was you'd be using into() rather than hard-calling the type (which should already be defined as a type in your function).
Remember, the whole point of conversion traits is to define them so you don't have to explicitly call them.
A final "demo" version of the code in working order could look like this:
extern crate git2;
use git2::Repository;
#[derive(Debug)] // Allow the use of "{:?}" format specifier
pub enum CustomError {
Git(git2::Error),
Other
}
impl From<(git2::Error)> for CustomError {
fn from(cause: git2::Error) -> Self {
CustomError::Git(cause)
}
}
fn test() -> Result<(), CustomError> {
let path = "foo";
let output = Repository::open(path)?;
Ok(())
}
fn main() {
println!("Hello, world!");
}