What would be the most idiomatic way to write tests for a CLI program using clap? I'm currently doing it like this:
#[derive(Debug, Parser)]
#[clap(author, version, about)]
pub struct StructArgs {
#[clap(subcommand)]
pub command_type: CommandType,
}
#[derive(Debug, Subcommand)]
pub enum CommandType {
Command1(Command1Args),
...
}
#[derive(Debug, Args)]
pub struct Command1Args {
pub field: String,
...
}
impl Command1Args {
...
}
#[test]
fn test_do_stuff() {
let args = StructArgs::try_parse_from(
std::iter::once("<PROGRAM NAME>")
.chain(
["<ARG 1>", ..., "<ARG n>"]
.iter()
.cloned()
)
);
if let CommandType::Command1(command1_args) = args.command_type {
// do stuff with command1_args
} else {
panic!();
}
}
Basically I pass to clap an iterator of arguments, then I check if the parsed command structure matches with the CommandType I expect, and I proceed to test its methods and internal state. The panic in the else branch is for failing the test if for some reason I get an unexpected CommandType, meaning most likely that I have written something wrong in the iterator.
Can this be improved further?
Related
I'm currently writing some unsafe code that requires some degree of manually managing lifetimes, and exposing those lifetimes to external users. I've been writing tests, and while it's pretty easy to write tests to verify the correctness of code that compiles (at least to the extent that the potential for undefined behavior allows), I've been looking to ensure certain types of unsound code can't compile.
Say I have the following contrived example:
pub struct MyBox<T> {
ptr: *mut T,
}
impl<T> MyBox<T> {
fn new(t: T) -> Self {
MyBox {
ptr: Box::into_raw(Box::new(t)),
}
}
fn get(&self) -> &T {
unsafe { &*self.ptr }
}
}
impl<T> Drop for MyBox<T> {
fn drop(&mut self) {
unsafe { Box::from_raw(self.ptr) }
}
}
fn test<'a>() -> &'a i32 {
MyBox::new(7).get()
}
How can I ensure that the test function continues to fail to compile from rust's test framework?
Obviously, just throwing the erroneous code into a separate file and using the actual test function as glorified build script works, but that gets really boilerplatey and awkward, especially when there are more than just one or two bad examples I would like to test. If it helps, I don't care about the specifics of the error, just that an error exists.
You can write it as a doctest with compile_fail attr so that you can write multiple tests that expect to fail at the same time in a comment.
/// ```compile_fail,E0515
/// use path_to::MyBox;
///
/// fn test<'a>() -> &'a i32 {
/// MyBox::new(7).get()
/// }
/// ```
fn _doc_test() {}
,E0515 indicates the error number you expect, which avoids any other errors that would make the test false negative. (This error number check will be ignored if cargo is not running as nightly)
Note that this requires your crate to be a lib instead of a bin, as bin is not currently supported by doctests. (issue #50784)
Another way, if you do care about the specific error the compiler emits, is to use trybuild.
You can write your test function in a doc-test with the compile_fail annotation:
pub struct MyBox<T> {
ptr: *mut T,
}
/// ```compile_fail
/// fn test<'a>() -> &'a i32 {
/// playground::MyBox::new(7).get()
/// }
/// ```
impl<T> MyBox<T> {
fn new(t: T) -> Self {
MyBox {
ptr: Box::into_raw(Box::new(t)),
}
}
fn get(&self) -> &T {
unsafe { &*self.ptr }
}
}
impl<T> Drop for MyBox<T> {
fn drop(&mut self) {
unsafe { Box::from_raw(self.ptr) }
}
}
Playground
Note though that you can only test public functions that way.
So I have a function. And I want to test it. It takes a struct as param. And the struct has some trait implemented. This trait has a long running io method. I don't want this io method to actually go fetch data, I want to mock this io method and just return the result. I am a little lost about how this can be done. Here is my try (not working)
struct TestStruct {
a: u32,
b: u32,
}
pub trait TestTrait {
fn some_long_running_io_method(&self) -> u32 {
156
}
}
fn important_func(a: TestStruct) {
println!("a: {}", a.some_long_running_io_method());
}
impl TestTrait for TestStruct {
fn some_long_running_io_method(&self) -> u32 {
self.a + self.b
}
}
#[cfg(test)]
mod tests {
use super::*;
use mockall::predicate::*;
use mockall::*;
#[cfg(test)]
mock! {
pub TestStruct {}
impl TestTrait for TestStruct {
fn some_long_running_io_method(&self) -> u32;
}
}
#[test]
fn test_important_func() {
let mut mock = MockTestStruct::new();
mock.expect_some_long_running_io_method()
.returning(|| 1);
important_func(mock);
}
}
I obviously get this error:
error[E0308]: mismatched types
--> src/test.rs:35:24
|
35 | important_func(mock);
| -------------- ^^^^ expected struct `TestStruct`, found struct `MockTestStruct`
| |
| arguments to this function are incorrect
How can I achieve mocking trait methods? 1) One way is to change function param and instead of accepting a concrete struct accept trait. And implement this trait on MockTestStruct. But then we have dynamic dispatching and it hurts the performance. I don't want a performance degrade just for the test. 2) I also tried reimplementing the Trait right where the test is, but conflicting implementations are not allowed in Rust. 3) Make function accept TestStruct or MockTestStruct? Probably not great way either.
Could you please tell me what is the idiomatic way to do it?
You can make your function, important_func a generic function. You can then use generic bounds to restrict the type to implementors of your trait.
Here is an example with your code:
struct TestStruct {
a: u32,
b: u32,
}
pub trait TestTrait {
fn some_long_running_io_method(&self) -> u32 {
156
}
}
// important_func can now use any type T which implements TestTrait,
// including your mock implementation!
fn important_func<T: TestTrait>(a: T) {
println!("a: {}", a.some_long_running_io_method());
}
impl TestTrait for TestStruct {
fn some_long_running_io_method(&self) -> u32 {
self.a + self.b
}
}
#[cfg(test)]
mod tests {
use super::*;
use mockall::predicate::*;
use mockall::*;
#[cfg(test)]
mock! {
pub TestStruct {}
impl TestTrait for TestStruct {
fn some_long_running_io_method(&self) -> u32;
}
}
#[test]
fn test_important_func() {
let mut mock = MockTestStruct::new();
mock.expect_some_long_running_io_method().returning(|| 1);
important_func(mock);
}
}
I have two error types that are defined in external crates. Let's call them foo::Error and bar::Error. Because they are both defined externally, I cannot create a direct From or Into conversion between the two.
In my own crate, I'm using implementing a trait from bar, which expects return values of Result<T, bar::Error>. In the methods of my implementation, I'm calling functions from foo, which return many Result<T, foo::Error> types.
I would like to map these foo::Error errors to bar::Error errors, so that my trait implementation is neat, but the best that I can do is create a separate shim error, and then have a clunky .map_err(ShimErr::from)? on everything. This can start to litter up my code if I'm calling many foo:: functions in my implementation.
use foo;
use bar;
struct ShimErr(foo::Error);
impl From<foo::Error> for ShimErr {
fn from(e: foo::Error) -> Self {
Self(e)
}
}
impl From<ShimErr> for bar::Error {
fn from(e: ShimErr) -> Self {
Self{}
}
}
struct MyTraitImpl {}
impl bar::SomeTrait for MyTraitImpl {
fn do_something() -> Result<i32, bar::Error> {
// FIXME: THIS IS CLUNKY
let foo_val: i32 = foo::call_something().map_err(ShimErr::from)?;
Ok(foo_val * 2)
}
}
Is there a better way? In an ideal world, there would be a way to just use ?
If I remove map_err and try to use ? directly:
error[E0277]: `?` couldn't convert the error to `bar::Error`
--> shimtest.rs:32:49
|
32 | let foo_val: i32 = foo::call_something()?;
| ^ the trait `std::convert::From<foo::Error>` is not implemented for `bar::Error`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following implementations were found:
<bar::Error as std::convert::From<ShimErr>>
= note: required by `std::convert::From::from`
The way I handle multiple different errors from other crates would be to make my own Error enum and use that instead of other errors directly.
So instead of a tuple struct that only contains foo::Error, you can create an enum that can have both foo::Error and bar::Error. After this, you can convert all the errors to the custom enum error with ?.
In certain cases, I think you'll still need to use something like .map_err(Into::into) occasionally, but usually only when the variable being returned is a Result with an external error.
use bar::Error as BarError;
use foo::Error as FooError;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::result::Result as StdResult;
/// Common result type that uses the custom error
pub type Result<T> = StdResult<T, Error>;
/// Common error type used as a holder for errors from various other libraries.
#[derive(Debug)]
pub enum Error {
Bar(BarError),
Foo(FooError),
}
impl From<BarError> for Error {
fn from(err: BarError) -> Error {
Error::Bar(err)
}
}
impl From<FooError> for Error {
fn from(err: FooError) -> Error {
Error::Foo(err)
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match *self {
Error::Bar(ref inner) => inner.fmt(f),
Error::Foo(ref inner) => inner.fmt(f),
}
}
}
impl StdError for Error {
fn description(&self) -> &str {
match *self {
Error::Bar(ref inner) => inner.description(),
Error::Foo(ref inner) => inner.description(),
}
}
}
Now you can use the custom Result and Error as follows.
// this Result is now our custom Result type with custom error
// same as std::result::Result<i32, Error> (with Error as our custom error)
fn do_something() -> Result<i32> {
let foo_val: i32 = foo::call_something()?; // should convert to our custom error
Ok(foo_val * 2)
}
fn do_something_else() -> Result<i32> {
// directly returning a Result<> with a different error type will need map_err
foo::call_something().map_err(Into::into)
}
I have a type Foo whose methods may "raise" errors of an associated type Foo::Err.
pub trait Foo {
type Err;
fn foo(&mut self) -> Result<(), Self::Err>;
}
I have another trait Bar with a method intended to process a Foo. Bar may issue errors of its own (specified by an associated type Bar::Err), but it may also encounter errors generated by the Foo it is processing.
I can see two ways to do this, but I don't know which one would be the most idiomatic to Rust.
The first one embeds a result in a result:
pub trait Bar1 {
type Err;
fn bar<F: Foo>(&mut self, foo: F) -> Result<Result<F, F::Err>, Self::Err>;
}
The second one merges the two error types into a dedicated enum:
pub trait Bar2 {
type Err;
fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Choice<F::Err, Self::Err>>;
}
The second one looks semantically cleaner, but creates some hurdles for handling the additional enum.
playground
Typically you don't do a "merge", but instead use nested errors, like this.
enum IntError {
Overflow,
Underflow
}
enum StrError {
TooLong,
TooShort,
}
enum GenericError {
Int(IntError),
Str(StrError),
}
impl From<IntError> for GenericError {
fn from(e: IntError) -> Self {
GenericError::Int(e)
}
}
impl From<StrError> for GenericError {
fn from(e: StrError) -> Self {
GenericError::Str(e)
}
}
You should use a trait object Error, and you return the first error that you encounter:
pub trait Bar {
fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Box<dyn Error>>;
}
or implement your trait like this:
impl Bar for MyType {
type Err = Box<dyn Error>;
fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Self::Err>;
}
If you really want to have your two errors (but this is strange because one error suffices to make the process not ok), you can use a crate like failure to create an "error trace".
As a general advice, you should not forget to use the traits from std to add more semantic to your code.
I want to implement the fmt::Display for a nested struct commonly used in my code.
// The root structure
pub struct WhisperFile<'a> {
pub path: &'a str,
pub handle: RefCell<File>,
pub header: Header
}
pub struct Header{
pub metadata: metadata::Metadata,
pub archive_infos: Vec<archive_info::ArchiveInfo>
}
pub struct Metadata {
// SNIP
}
pub struct ArchiveInfo {
// SNIP
}
As you can see, this is a non-trivial tree of data. The archive_infos property on Header can be quite long when presented as one line.
I would like to emit something along the lines of
WhisperFile ({PATH})
Metadata
...
ArchiveInfo (0)
...
ArchiveInfo (N)
...
But when I try to display Vec<ArchiveInfo> I get that Display is not implemented. I can implement fmt::Display for ArchiveInfo but that's not enough since fmt::Display is not implemented for the parent container Vec. If I implement fmt::Display for collections::vec::Vec<ArchiveInfo> I get the impl does not reference any types defined in this crate; only traits defined in the current crate can be implemented for arbitrary types.
I have tried iterating over the vec and calling write!() but I couldn't figure out what the control flow should look like. I think write!() needs to be return value of the function but that breaks down with multiple calls.
How can I pretty print a Vec of my structures?
As this error states, you cannot implement a trait for a type you don't own:
the impl does not reference any types defined in this crate; only traits defined in the current crate can be implemented for arbitrary types
However, you can implement Display for your wrapper type. The piece you are missing is to use the try! macro or the try operator ?:
use std::fmt;
struct Foo(Vec<u8>);
impl fmt::Display for Foo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Values:\n")?;
for v in &self.0 {
write!(f, "\t{}", v)?;
}
Ok(())
}
}
fn main() {
let f = Foo(vec![42]);
println!("{}", f);
}