Comparing functions for equality in Rust - unit-testing

I have a function which takes a number as an argument, and then returns a function based on the number. Depending on many different things, it might return any of ~50 functions, and the cases for which one it should return get pretty complicated. As such, I want to build some tests to make sure the proper functions are being returned. What I have so far looks roughly like this.
fn pick_a_function(decider: u32) -> fn(&mut SomeStruct) {
match decider {
1 => add,
2 => sub,
_ => zero,
}
}
fn add(x: &mut SomeStruct) {
x.a += x.b;
}
fn sub(x: &mut SomeStruct) {
x.a -= x.b;
}
fn zero(_x: &mut SomeStruct) {
x.a = 0;
}
fn main() {
let mut x = SomeStruct { a: 2, b: 3 };
pick_a_function(1)(&mut x);
println!("2 + 3 = {}", x.a);
}
#[cfg(test)]
mod tests {
use super::*;
fn picks_correct_function() {
assert_eq!(pick_a_function(1), add);
}
}
The problem is that the functions don't seem to implement the Eq or PartialEq traits, so assert_eq! just says that it can't compare them. What options do I have for comparing the returned function to the correct function?

So it turns of that functions in Rust actually do implement PartialEq as long as there is not a lifetime attached, and as long as the function takes less than 10 arguments. This restriction is because each form of function signature has to have the traits implemented directly, because the compiler considers all of them to be completely unrelated types.
The functions I was returning took a mutable reference to a struct, which implicitly gives the function a lifetime, so they no longer had a type signature which implemented PartialEq. All that rust really does internally to compare function equality though is cast both of them to pointers and then compare, so we can actually just do the same thing.
#[cfg(test)]
mod tests {
use super::*;
fn picks_correct_function() {
assert_eq!(
pick_a_function(1) as usize,
add as usize
);
}
}

You should compare the result instead of the function,for example:
#[cfg(test)]
mod tests {
use super::*;
fn picks_correct_function() {
let add_picked = pick_a_function(1);
assert_eq!(add_picked(1,2), add(1,2));
}
}
Or in more complex scenarios you can compare the inputs making a function that takes one parameter and another that takes two,try to call any of them and see if you get a compiler error.

Related

How to pass parameters generated by a method to a Rust unit test?

I'm trying to write a parametrized unit test in Rust, which will take parameters from a method. I know how to use rstest when parameters are hardcoded:
use rstest::rstest;
#[rstest]
#[case(42)]
#[case(256)]
fn my_test(#[case] x: i32) {
assert!(x > 0)
}
However, I want something like this:
#[cases_from(my_provider)]
fn my_test(#[case] x: i32) {
assert!(x > 0)
}
fn my_provider() -> Vec<i32> {
[42, 256].to_vec()
}
Is is possible with rstest? If not, which library enables this?
It does not appear to be possible with rstest, though a similar feature has been asked for: Issue #39: Dynamic set of tests. There is difficulty because the library is designed around generating individual test functions that are then ran and displayed separately by the standard cargo test harness. And to do that, the procedural macros at play need all the pieces. It is not implemented by a simple for-loop over the test cases.
If you simply desire a mechanism to use the same set of test cases for multiple tests, the developer has created the rstest-reuse crate which would be used like this:
#[template]
#[rstest]
#[case(2, 2)]
#[case(4/2, 2)]
fn two_simple_cases(#[case] a: u32, #[case] b: u32) {}
#[apply(two_simple_cases)]
fn it_works(a: u32, b: u32) {
assert!(a == b);
}
#[apply(two_simple_cases)]
fn it_fail(a: u32, b: u32) {
assert!(a != b);
}
If you're fine with a single test that loops over inputs (and therefore loses some visibility into which test failed and functionality to continue with other cases), then you can simply write that yourself:
#[test]
fn my_test() {
fn my_test_inner(x: i32) {
assert!(x > 0)
}
for x in my_provider() {
my_test_inner(x);
}
}
fn my_provider() -> Vec<i32> {
[42, 256].to_vec()
}

How to compose two calls to Regex::replace_all?

Regex::replace_all has the signature fn (text: &str) -> Cow<str>. How would two calls to this be written, f(g(x)), giving the same signature?
Here's some code I'm trying to write. This has the two calls separated out into two functions, but I couldn't get it working in one function either. Here's my lib.rs in a fresh Cargo project:
#![allow(dead_code)]
/// Plaintext and HTML manipulation.
use lazy_static::lazy_static;
use regex::Regex;
use std::borrow::Cow;
lazy_static! {
static ref DOUBLE_QUOTED_TEXT: Regex = Regex::new(r#""(?P<content>[^"]+)""#).unwrap();
static ref SINGLE_QUOTE: Regex = Regex::new(r"'").unwrap();
}
fn add_typography(text: &str) -> Cow<str> {
add_double_quotes(&add_single_quotes(text)) // Error! "returns a value referencing data owned by the current function"
}
fn add_double_quotes(text: &str) -> Cow<str> {
DOUBLE_QUOTED_TEXT.replace_all(text, "“$content”")
}
fn add_single_quotes(text: &str) -> Cow<str> {
SINGLE_QUOTE.replace_all(text, "’")
}
#[cfg(test)]
mod tests {
use crate::{add_typography};
#[test]
fn converts_to_double_quotes() {
assert_eq!(add_typography(r#""Hello""#), "“Hello”");
}
#[test]
fn converts_a_single_quote() {
assert_eq!(add_typography("Today's Menu"), "Today’s Menu");
}
}
Here's the best I could come up with, but this will get ugly fast when composing three or four functions:
fn add_typography(input: &str) -> Cow<str> {
match add_single_quotes(input) {
Cow::Owned(output) => add_double_quotes(&output).into_owned().into(),
_ => add_double_quotes(input),
}
}
A Cow contains maybe-owned data.
We can infer from what the replace_all function does that it returns borrowed data only if substitutions did not happen, otherwise it has to return new, owned data.
The problem arises when the inner call makes a substitution but the outer one does not. In that case, the outer call will simply pass its input through as Cow::Borrowed, but it borrows from the Cow::Owned value returned by the inner call, whose data now belongs to a Cow temporary that is local to add_typography(). The function would therefore return a Cow::Borrowed, but would borrow from the temporary, and that's obviously not memory-safe.
Basically, this function will only ever return borrowed data when no substitutions were made by either call. What we need is a helper that can propagate owned-ness through the call layers whenever the returned Cow is itself owned.
We can construct a .map() extension method on top of Cow that does exactly this:
use std::borrow::{Borrow, Cow};
trait CowMapExt<'a, B>
where B: 'a + ToOwned + ?Sized
{
fn map<F>(self, f: F) -> Self
where F: for <'b> FnOnce(&'b B) -> Cow<'b, B>;
}
impl<'a, B> CowMapExt<'a, B> for Cow<'a, B>
where B: 'a + ToOwned + ?Sized
{
fn map<F>(self, f: F) -> Self
where F: for <'b> FnOnce(&'b B) -> Cow<'b, B>
{
match self {
Cow::Borrowed(v) => f(v),
Cow::Owned(v) => Cow::Owned(f(v.borrow()).into_owned()),
}
}
}
Now your call site can stay nice and clean:
fn add_typography(text: &str) -> Cow<str> {
add_single_quotes(text).map(add_double_quotes)
}

Making a mock implementation record any number of arguments

Context & my attempts
I am trying to unit-test a function, and I want to make sure it calls the correct API endpoint with the correct arguments.
My first idea was to make the function I wanted to test accept the API as a trait object. This would allow me to mock it. I also had to pass mutable references, because the mock should record information about how it was called. Below you see a simplified representation of my code structure:
trait API {
fn func_a(&mut self, arg1: i32, arg2: String);
}
// prod trait implementation
#[allow(dead_code)]
fn business_logic(api: &mut dyn API) {
api.func_a(42, String::from("mytrousersareblue"));
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Default)]
struct Mock<A, B> {
args: (A, B),
}
impl API for Mock<i32, String> {
fn func_a(&mut self, arg1: i32, arg2: String) {
self.args = (arg1, arg2);
}
}
#[test]
fn business_logic_performs_correct_calls() {
let mut mock: Mock<i32, String> = Default::default();
business_logic(&mut mock);
assert_eq!(mock.args.0, 42);
assert_eq!(mock.args.1, String::from("mytrousersareblue"));
}
}
This has various downsides, mainly that I have to make separate Mock structs for functions with zero, one, two, three, and more parameters.
So another idea that came to my mind was to implement the Mock struct as follows:
struct Mock<'a> {
args: Vec<&'a dyn Eq>,
}
This would allow me to record as many arguments as I want. However the compiler complains that the trait std::cmp::Eq cannot be made into an object.
I cannot use std::any::Any, because I still need to be able to determine equality in the assert_eq statements.
Maybe I am taking a completely wrong path here. I do have quite some trouble with the Rust mindset, coming from a Java background...
Summary / TLDR
I want to have a mock-implementation of the API, which records all arguments it was called with, that can later be compared against expected values.
If I'm correct, then I'll need a way to make a struct store any number of objects, which all extend at least Eq. However, if there's a nicer solution: I'm open.
I've found two solutions:
The practical one:
Use the mockall crate (thanks #Richard Matheson for the recommendation). Seriously, don't try to re-implement mocking yourself. The mockall crates does it using macros, and it is pure magic. But it works.
The nightmare:
I don't recommend using this answer, but it is helpful to know as a design concept.
The way to solve this problem, is to declare a trait implementing Any and defining the following functions:
trait Arg: Any {
fn as_any(&self) -> &dyn Any;
fn equals_arg(&self, _: &dyn Any) -> bool;
}
Then you implement this trait for as many structs as possible:
impl<S: 'static + PartialEq> Arg for S {
fn as_any(&self) -> &dyn Any {
self
}
fn equals_arg(&self, other: &dyn Arg) -> bool {
other
.as_any()
.downcast_ref::<S>()
.map_or(false, |a| self == a)
}
}
Implementing the as_any function is straightforward. The equals_arg function is trickier. We compare these two references by first trying to downcast the other object to the type of self. If that fails, we immediately return false. If it succeeds, we compare them using the == operator, made available because both implement the PartialEq trait.
We can now store an array of dyn Arg elements and compare them using equals_arg. See the playground for an example implementation. (In the playground I changed the API interface to accept &'static types. This could certainly be improved, but I am a beginner concerning lifetimes.)
References
https://dev.to/magnusstrale/rust-trait-objects-in-a-vector-non-trivial-4co5
How to test for equality between trait objects?

How to unit-test a deserialization function used in serde(deserialize_with)?

I have a struct which implements Deserialize and uses the serde(deserialize_with) on a field:
#[derive(Debug, Deserialize)]
struct Record {
name: String,
#[serde(deserialize_with = "deserialize_numeric_bool")]
is_active: bool,
}
The implementation of deserialize_numeric_bool deserializes a string "0" or "1" to the corresponding boolean value:
pub fn deserialize_numeric_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
where D: Deserializer<'de>
{
struct NumericBoolVisitor;
impl<'de> Visitor<'de> for NumericBoolVisitor {
type Value = bool;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("either 0 or 1")
}
fn visit_u64<E>(self, value: u64) -> Result<bool, E>
where E: DeserializeError
{
match value {
0 => Ok(false),
1 => Ok(true),
_ => Err(E::custom(format!("invalid bool: {}", value))),
}
}
}
deserializer.deserialize_u64(NumericBoolVisitor)
}
(I appreciate comments about code improvements)
I'd like to write unit tests for deserialization functions like deserialize_numeric_bool. Of course, my friendly search box revealed the serde_test crate and a documentation page about unit-testing.
But these resources couldn't help me in my case, as the crate tests a structure directly implementing Deserialize.
One idea I had was to create a newtype which only contains the output of my deserialize functions and test it with it. But this looks like a unnecessary indirection to me.
#[derive(Deserialize)]
NumericBool {
#[serde(deserialize_with = "deserialize_numeric_bool")]
value: bool
};
How do I write idiomatic tests for it?
My current solution uses only structures already provided by serde.
In my use case, I only wanted to test that a given string will deserialize successfully into a bool or has a certain error. The serde::de::value provides simple deserializers for fundamental data types, for example U64Deserializer which holds a u64. It also has an Error struct which provides a minimal representation for the Error traits – ready to be used for mocking errors.
My tests look currently like that: I mock the input with a deserializer and pass it to my function under test. I like that I don't need an indirection there and that I have no additional dependencies. It is not as nice as the assert_tokens* provided serde_test, as it needs the error struct and feels less polished. But for my case, where only a single value is deserialized, it fulfills my needs.
use serde::de::IntoDeserializer;
use serde::de::value::{U64Deserializer, StrDeserializer, Error as ValueError};
#[test]
fn test_numeric_true() {
let deserializer: U64Deserializer<ValueError> = 1u64.into_deserializer();
assert_eq!(numeric_bool(deserializer), Ok(true));
}
#[test]
fn test_numeric_false() {
let deserializer: U64Deserializer<ValueError> = 0u64.into_deserializer();
assert_eq!(numeric_bool(deserializer), Ok(false));
}
#[test]
fn test_numeric_invalid_number() {
let deserializer: U64Deserializer<ValueError> = 2u64.into_deserializer();
let error = numeric_bool(deserializer).unwrap_err();
assert_eq!(error.description(), "invalid bool: 2");
}
#[test]
fn test_numeric_empty() {
let deserializer: StrDeserializer<ValueError> = "".into_deserializer();
let error = numeric_bool(deserializer).unwrap_err();
assert_eq!(error.description(), "invalid type: string \"\", expected either 0 or 1");
}
I hope that it helps other folks too or inspire other people to find a more polished version.
I've come across this question several times while trying to solve a similar problem recently. For future readers, pixunil's answer is nice, straightforward, and works well. However, I'd like to provide a solution using serde_test as the unit testing documentation mentions.
I researched how serde_test is used across a few crates that I found via its reverse dependencies on lib.rs. Several of them define small structs or enums for testing deserialization or serialization as you mentioned in your original post. I suppose doing so is idiomatic when testing would be too verbose otherwise.
Here's a few examples; this is a non-exhaustive list:
Example from time
Another example from time
Example from slab (tokio)
Example from bitcoin_hashes
Example from uuid
Example from euclid
Anyway, let's say I have a function to deserialize a bool from a u8 and another function that serializes a bool to a u8.
use serde::{
de::{Error as DeError, Unexpected},
Deserialize, Deserializer, Serialize, Serializer,
};
fn bool_from_int<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: Deserializer<'de>,
{
match u8::deserialize(deserializer)? {
0 => Ok(false),
1 => Ok(true),
wrong => Err(DeError::invalid_value(
Unexpected::Unsigned(wrong.into()),
&"zero or one",
)),
}
}
#[inline]
fn bool_to_int<S>(a_bool: &bool, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if *a_bool {
serializer.serialize_u8(1)
} else {
serializer.serialize_u8(0)
}
}
I can test those functions by defining a struct in my test module. This allows constraining the tests to those functions specifically instead of ser/deserializing a larger object.
#[cfg(test)]
mod tests {
use super::{bool_from_int, bool_to_int};
use serde::{Deserialize, Serialize};
use serde_test::{assert_de_tokens_error, assert_tokens, Token};
#[derive(Debug, PartialEq, Deserialize, Serialize)]
#[serde(transparent)]
struct BoolTest {
#[serde(deserialize_with = "bool_from_int", serialize_with = "bool_to_int")]
a_bool: bool,
}
const TEST_TRUE: BoolTest = BoolTest { a_bool: true };
const TEST_FALSE: BoolTest = BoolTest { a_bool: false };
#[test]
fn test_true() {
assert_tokens(&TEST_TRUE, &[Token::U8(1)])
}
#[test]
fn test_false() {
assert_tokens(&TEST_FALSE, &[Token::U8(0)])
}
#[test]
fn test_de_error() {
assert_de_tokens_error::<BoolTest>(
&[Token::U8(14)],
"invalid value: integer `14`, expected zero or one",
)
}
}
BoolTest is within the tests module which is gated by #[cfg(test)] as per usual. This means that BoolTest is only compiled for tests rather than adding bloat. I'm not a Rust expert, but I think this is a good alternative if a programmer wishes to use serde_test as a harness.

Creating an `std::env::Args` iterator for testing

Is there a way in Rust to create a std::env::Args from a Vec<String> in order to use it in a #[test] function?
I wish to test a function that gets a std::env::Args as an argument, but I don't know how to create such an object with a list of arguments I supply for the test.
I wasn't able to figure this one out from the docs, the source nor from Google searches.
The fields of std::env::Args are not documented, and there doesn't appear to be a public function to create one with custom fields. So, you're outta luck there.
But since it's just "An iterator over the arguments of a process, yielding a String value for each argument" your functions can take a String iterator or Vec without any loss of functionality or type safety. Since it's just a list of Strings, it doesn't make much sense to arbitrarily limit your functions to strings which happen to come from the command line.
Looking through Rust's own tests, that's just what they do. There's a lot of let args: Vec<String> = env::args().collect();
There's even an example in rustbuild where they strip off the name of the program and just feed the list of arguments.
use std::env;
use bootstrap::{Config, Build};
fn main() {
let args = env::args().skip(1).collect::<Vec<_>>();
let config = Config::parse(&args);
Build::new(config).build();
}
And bootstrap::Config::parse() looks like so:
impl Config {
pub fn parse(args: &[String]) -> Config {
let flags = Flags::parse(&args);
...
I'm not a Rust expert, but that seems to be how the Rust folks handle the problem.
#Schwern's answer is good and it led me to this simpler version. Since std::env::Args implements Iterator with Item = String you can do this:
use std::env;
fn parse<T>(args: T)
where
T: Iterator<Item = String>,
{
for arg in args {
// arg: String
print!("{}", arg);
}
}
fn main() {
parse(env::args());
}
To test, you provide parse with an iterator over String:
#[test]
fn test_parse() {
let args = ["arg1", "arg2"].iter().map(|s| s.to_string());
parse(args);
}
I've wrote a little macro to make this easier, based on #Rossman's answer (and therefore also based on #Schwern's answer; thanks go to both):
macro_rules! make_string_iter {
($($element: expr), *) => {
{
let mut v = Vec::new();
$( v.push(String::from($element)); )*
v.into_iter()
}
};
}
It can be used in that way:
macro_rules! make_string_iter {
($($element: expr), *) => {
{
let mut v = Vec::new();
$( v.push(String::from($element)); )*
v.into_iter()
}
};
}
// We're using this function to test our macro
fn print_args<T: Iterator<Item = String>>(args: T) {
for item in args {
println!("{}", item);
}
}
fn main() {
// Prints a, b and c
print_args(make_string_iter!("a", "b", "c"))
}
Or try it out on the Rust Playground.
I'm not (yet) an expert in rust, any suggestions are highly welcome :)