Consider the following function:
pub async fn api_get<T: 'static>(&self, path: &str) -> reqwest::Result<T>
where
T: serde::de::DeserializeOwned,
{
let url = format!("{}/{}", self.backend_url, path);
let response = self.client.get(&url).send().await?;
let data = response.json::<T>().await?;
Ok(data)
}
How can I mock the request::Result type to return an Error type type?
I Tried the following:
#[tokio::test]
async fn foo_test() {
let mock_client = HttpClient::default();
mock_client
.expect_api_post()
.returning(|_, __| reqwest::Error::new(reqwest::ErrorKind::Other, "test error"));
}
However this does not work as the new method is private. So how can I mock the return type of this function?
(In this case the api_post method is declared in the impl block of the struct HttpClient)
Related
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'm new to Rust and going through the official book. I'm working on a simple grep example and want to make an exit function which I can use in different places. Unfortunately using this function in a closure in unwrap_or_else causes a compile error. This not clear to me why, because when I use the contents of the function directly in the closure it works.
Here is my main.rs file:
use std::env;
use std::fs;
use std::process;
use std::error::Error;
use std::fmt::Display;
struct Config{
query: String,
filename: String,
}
impl Config {
fn new(input: &[String]) -> Result<Config, &'static str> {
if input.len() < 3 {
return Err("Not enough arguments provided.");
}
let query = input[1].clone();
let filename = input[2].clone();
Ok(Config { query, filename })
}
}
fn run(cfg: Config) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(&cfg.filename)?;
contents.find(&cfg.query).expect("Corrupted text file.");
Ok(())
}
fn exit<T: Display>(msg: &str, err: T) {
println!("{}: {}", msg, err);
process::exit(1);
}
fn main() {
let args: Vec<String> = env::args().collect();
println!("{:?}", args);
let cfg = Config::new(&args).unwrap_or_else(|err| {
exit("Problem parsing arguments", err);
});
if let Err(err) = run(cfg) {
exit("Application error", err);
}
}
And here is the compile error:
error[E0308]: mismatched types
--> src\main.rs:41:55
|
41 | let cfg = Config::new(&args).unwrap_or_else(|err| {
| _______________________________________________________^
42 | | exit("Problem parsing arguments", err);
43 | | });
| |_____^ expected struct `Config`, found `()`
When I change the Config::new(&args).unwrap_or_else closure to this, it works:
let cfg = Config::new(&args).unwrap_or_else(|err| {
println!("Problem parsing arguments: {}", err);
process::exit(1);
});
I got stuck on this too. You need to import the process library:
use std::process;
edit: On second look you did import it. For others who run into this problem then that was mine. I got the same error.
You need to specify, that your exit() function never returns, i.e. add -> !.
These functions are called "diverging functions".
fn exit<T: Display>(msg: &str, err: T) -> ! {
println!("{}: {}", msg, err);
process::exit(1);
}
However, you should be careful with using process::exit(). Because it will terminate the current process, and not invoke destructors.
To ensure destructors are handled, you should instead do something like this:
fn main() {
std::process::exit(match run() {
Ok(_) => 0,
Err(code) => code,
});
}
fn run() -> Result<(), i32> {
// Application logic here, i.e. what you'd otherwise have had in `main()`
Ok(())
}
The example is a minor adapted version of the one found at the documentation for process::exit().
To add to vallentin's answer here's the more idiomatic version which doesn't use process::exit:
use std::env;
use std::error::Error;
use std::fmt::Display;
use std::fs;
use std::process;
struct Config {
query: String,
filename: String,
}
impl Config {
fn new(input: &[String]) -> Result<Config, &'static str> {
if input.len() < 3 {
return Err("Not enough arguments provided.");
}
let query = input[1].clone();
let filename = input[2].clone();
Ok(Config { query, filename })
}
}
fn run(cfg: Config) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(&cfg.filename)?;
// convert Option to a Result so we can use `?`
contents.find(&cfg.query).ok_or("Corrupted text file.")?;
Ok(())
}
// you can return a Result from main and Rust will
// print the error to the user if there is one
fn main() -> Result<(), Box<dyn Error>> {
let args: Vec<String> = env::args().collect();
println!("{:?}", args);
// use `?` instead of `exit` function
let cfg = Config::new(&args)?;
run(cfg)?;
Ok(())
}
playground
Here is as far as I could get, using rental, partly based on How can I store a Chars iterator in the same struct as the String it is iterating on?. The difference here is that the get_iter method of the locked member has to take a mutable self reference.
I'm not tied to using rental: I'd be just as happy with a solution using reffers or owning_ref.
The PhantomData is present here just so that MyIter bears the normal lifetime relationship to MyIterable, the thing being iterated over.
I also tried changing #[rental] to #[rental(deref_mut_suffix)] and changing the return type of MyIterable.get_iter to Box<Iterator<Item=i32> + 'a> but that gave me other lifetime errors originating in the macro that I was unable to decipher.
#[macro_use]
extern crate rental;
use std::marker::PhantomData;
pub struct MyIterable {}
impl MyIterable {
// In the real use-case I can't remove the 'mut'.
pub fn get_iter<'a>(&'a mut self) -> MyIter<'a> {
MyIter {
marker: PhantomData,
}
}
}
pub struct MyIter<'a> {
marker: PhantomData<&'a MyIterable>,
}
impl<'a> Iterator for MyIter<'a> {
type Item = i32;
fn next(&mut self) -> Option<i32> {
Some(42)
}
}
use std::sync::Mutex;
rental! {
mod locking_iter {
pub use super::{MyIterable, MyIter};
use std::sync::MutexGuard;
#[rental]
pub struct LockingIter<'a> {
guard: MutexGuard<'a, MyIterable>,
iter: MyIter<'guard>,
}
}
}
use locking_iter::LockingIter;
impl<'a> Iterator for LockingIter<'a> {
type Item = i32;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.rent_mut(|iter| iter.next())
}
}
struct Access {
shared: Mutex<MyIterable>,
}
impl Access {
pub fn get_iter<'a>(&'a self) -> Box<Iterator<Item = i32> + 'a> {
Box::new(LockingIter::new(self.shared.lock().unwrap(), |mi| {
mi.get_iter()
}))
}
}
fn main() {
let access = Access {
shared: Mutex::new(MyIterable {}),
};
let iter = access.get_iter();
let contents: Vec<i32> = iter.take(2).collect();
println!("contents: {:?}", contents);
}
As user rodrigo has pointed out in a comment, the solution is simply to change #[rental] to #[rental_mut].
I have written a trait that specifies some methods similar to those of Vec:
pub trait Buffer {
type Item;
fn with_capacity(c: usize) -> Self;
fn push(&mut self, item: Self::Item);
}
I would like to implement FromIterator for all types that implement Buffer, as follows:
impl<T> iter::FromIterator<T::Item> for T
where T: Buffer
{
fn from_iter<I>(iter: I) -> Self
where I: IntoIterator<Item = T>
{
let mut iter = iter.into_iter();
let (lower, _) = iter.size_hint();
let ans = Self::with_capacity(lower);
while let Some(x) = iter.next() {
ans.push(x);
}
ans
}
}
The compiler won't let me:
error[E0210]: type parameter `T` must be used as the type parameter
for some local type (e.g. `MyStruct<T>`); only traits defined in the
current crate can be implemented for a type parameter
I think I understand the error message; it is preventing me from writing code that is incompatible with possible future changes to the standard library.
The only way around this error appears to be to implement FromIterator separately for every type for which I implement Buffer. This will involve copying out exactly the same code many times. Is there a a way to share the same implementation between all Buffer types?
You can't implement a trait from another crate for an arbitrary type, only for a type from your crate. However, you can move the implementation to a function and reduce amount of duplicated code:
fn buffer_from_iter<I, B>(iter: I) -> B
where I: IntoIterator<Item = B::Item>,
B: Buffer
{
let mut iter = iter.into_iter();
let (lower, _) = iter.size_hint();
let mut ans = B::with_capacity(lower);
while let Some(x) = iter.next() {
ans.push(x);
}
ans
}
struct S1;
impl Buffer for S1 {
type Item = i32;
fn with_capacity(c: usize) -> Self { unimplemented!() }
fn push(&mut self, item: Self::Item) { unimplemented!() }
}
impl std::iter::FromIterator<<S1 as Buffer>::Item> for S1 {
fn from_iter<I>(iter: I) -> Self
where I: IntoIterator<Item = <S1 as Buffer>::Item>
{
buffer_from_iter(iter)
}
}
This implementation of FromIterator can be wrapped into a macro to further reduce code duplication.
I have a match expression than can return several (builtin and custom) types, which will ultimately be serialized to JSON and returned from a web request. I would prefer to avoid repeating the serialization code or making a string copy in each match arm.
Each arm returns an Encodable; however, it seems that Encodable is not object-safe, so I cannot make a pointer to it.
Edit: Due to changes in Rust, the question has gone from "Is this a good way to do this?" to "How can I do this at all?" (This is with version rustc 1.0.0-nightly (ed530d7a3 2015-01-16 22:41:16 +0000))
extern crate "rustc-serialize" as rustc_serialize;
use rustc_serialize::{json, Encodable};
#[derive(RustcEncodable)]
struct Valid {
value: u32
}
#[derive(RustcEncodable)]
struct Error {
error: &'static str // '
}
fn main() {
let valid = true;
let result = match valid {
true => Box::new(Valid { value: 42 }) as Box<Encodable>,
false => Box::new(Error { error: "bork" }) as Box<Encodable>
};
let mut buf = String::new();
result.encode(&mut json::Encoder::new(&mut buf)).unwrap();
println!("{}", buf);
}
error: cannot convert to a trait object because trait `rustc-serialize::serialize::Encodable` is not object-safe [E0038]
There are 2 ways in which traits can be used in Rust:
As bounds in generic functions (static dispatch)
As trait objects, behind pointers (dynamic dispatch)
Because Encodable is not object-safe, we can't use dynamic dispatch, because the compiler doesn't allow us to create a pointer to an Encodable.
Therefore, we have to use static dispatch. To do this, I've moved the code that works on the Encodable to a new, generic function, and called it from each arm.
extern crate "rustc-serialize" as rustc_serialize;
use rustc_serialize::{json, Encodable};
#[derive(RustcEncodable)]
struct Valid {
value: u32
}
#[derive(RustcEncodable)]
struct Error {
error: &'static str // '
}
fn do_encode<E: Encodable>(e: E) -> () {
let mut buf = String::new();
e.encode(&mut json::Encoder::new(&mut buf)).unwrap();
println!("{}", buf);
}
fn main() {
let is_valid = true;
match is_valid {
true => do_encode(Valid { value: 42 }),
false => do_encode(Error { error: "bork" }),
};
}