Mock instance inside serde implementation - unit-testing

I'm trying to implement custom deserialization function/method which uses some external functionality. The function creates an instance and uses its methods. It's working fine, but I can't figure out how to mock the service in tests.
More general question is: how to provide a state to deserialization function/method?
The code below illustrates what I mean.
MagickBook is an external service which holds a state and contains some essential logic in MagickBook::find method.
Scroll is a deserializable data structure, which should be deserialized using the logic from MagicBook.
I'd like to have a way to provide particular instance of MagicBook from the outside, at the moment of deserialization. For example in tests.
Rust Playground
use serde::de::{Deserialize, Deserializer}; // 1.0.82
use serde_derive::Deserialize; // 1.0.82
use serde_json; // 1.0.33
struct MagickBook;
// Some service which I want to mock in the test
impl MagickBook {
fn new() -> Self {
Self {}
}
fn find(&self, _kind: &str) -> isize {
let effect = 42;
// Here we do some logic depending on input parameter
// -- snip --
return effect;
}
}
#[derive(Deserialize, PartialEq, Debug)]
struct Scroll {
#[serde(rename = "kind")]
#[serde(deserialize_with = "deserialize_effect")]
effect: isize,
}
fn deserialize_effect<'de, D>(deserializer: D) -> Result<isize, D::Error>
where
D: Deserializer<'de>,
{
let book = MagickBook::new();
Ok(book.find(&String::deserialize(deserializer)?))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn main() {
let scroll: Scroll = serde_json::from_str("{\"kind\":\"wind\"}").unwrap();
assert_eq!(scroll, Scroll { effect: 42 });
}
}

I would recommend accessing the mock through an internally mutable thread-local instance.
use serde::{Deserialize, Deserializer};
use std::cell::RefCell;
struct MagickBook {
value: isize,
}
thread_local! {
static MAGICK_BOOK: RefCell<MagickBook> = RefCell::new(MagickBook::new());
}
impl MagickBook {
fn new() -> Self {
MagickBook { value: 0 }
}
fn find(&self, _kind: &str) -> isize {
let effect = self.value;
// -- snip --
effect
}
}
#[derive(Deserialize, PartialEq, Debug)]
struct Scroll {
#[serde(rename = "kind", deserialize_with = "deserialize_effect")]
effect: isize,
}
fn deserialize_effect<'de, D>(deserializer: D) -> Result<isize, D::Error>
where
D: Deserializer<'de>,
{
let kind = String::deserialize(deserializer)?;
Ok(MAGICK_BOOK.with(|book| book.borrow().find(&kind)))
}
#[test]
fn test_deserialize() {
MAGICK_BOOK.with(|book| book.borrow_mut().value = 42);
let scroll: Scroll = serde_json::from_str(r#"{"kind":"wind"}"#).unwrap();
assert_eq!(scroll, Scroll { effect: 42 });
}

Related

Rust cache async traits

I'm running into an issue when I attempt to cache a value for as long as it's valid and update it when it becomes invalid. I believe the issue is due to my attempt to share state across async executions. Further, this component lives in a multi-threaded / concurrent environment.
The error I'm seeing that I don't know how to fix is
future is not `Send` as this value is used across an await
Following is a minimum example that I could come up with (it also features some ownership issues) that generally captures my use-case and the issue I'm seeing. Here is a playground of the code.
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use std::sync::{Arc, Mutex};
struct Creds {
expires_at: DateTime<Utc>,
}
impl Creds {
fn is_expired(&self) -> bool {
self.expires_at.le(&Utc::now())
}
}
#[async_trait]
trait CredsProvider {
async fn get_creds(&self) -> Creds;
}
struct MyCredsProvider {
cached_creds: Arc<Mutex<Option<Creds>>>,
}
impl MyCredsProvider {
fn new() -> Self {
MyCredsProvider {
cached_creds: Arc::new(Mutex::new(None)),
}
}
async fn inner_get_creds(&self) -> Creds {
todo!()
}
}
#[async_trait]
impl CredsProvider for MyCredsProvider {
async fn get_creds(&self) -> Creds {
let mg = self
.cached_creds
.lock()
.expect("Unable to get lock on creds mutex");
if mg.is_some() && !mg.as_ref().unwrap().is_expired() {
return mg.unwrap();
}
let new_creds = self.inner_get_creds().await;
*mg = Some(new_creds);
return new_creds;
}
}
#[tokio::main]
async fn main() {
MyCredsProvider::new();
// Some multi-threaded / concurrent logic to periodically refresh creds
todo!()
}
I wasn't sure how to include this in the example but in main imagine multiple worker threads running concurrently / parallel that each call CredsProvider.get_creds and then use these creds to perform some work (if you can add that to a complete working example, that'd be much appreciated for my edification). Assume MyCredsProvider.inner_get_creds is expensive and should only be called when the cached creds expire.
How do I solve this? I thought that the Arc<Mutex<>> would be enough but it seems not. At one point, I tried making Creds and trait so that I could have Arc<Mutex<Option<Box<dyn Creds + Send + Sync>>>> but that felt like the wrong path and didn't work.
Thanks.
You may would like to switch to tokio::sync::Mutex (playground).
It solves
future is not `Send` as this value is used across an await
Code:
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use std::sync::Arc;
use tokio::sync::Mutex;
#[derive(Clone)]
struct Creds {
expires_at: DateTime<Utc>,
}
impl Creds {
fn is_expired(&self) -> bool {
self.expires_at.le(&Utc::now())
}
}
#[async_trait]
trait CredsProvider {
async fn get_creds(&self) -> Creds;
}
struct MyCredsProvider {
cached_creds: Arc<Mutex<Option<Creds>>>,
}
impl MyCredsProvider {
fn new() -> Self {
MyCredsProvider {
cached_creds: Arc::new(Mutex::new(None)),
}
}
async fn inner_get_creds(&self) -> Creds {
todo!()
}
}
#[async_trait]
impl CredsProvider for MyCredsProvider {
async fn get_creds(&self) -> Creds {
let mut mg = self
.cached_creds
.lock()
.await;
if mg.is_some() && !mg.as_ref().unwrap().is_expired() {
return mg.clone().unwrap();
} else {
let new_creds = self.inner_get_creds().await;
*mg = Some(new_creds.clone());
return new_creds;
}
}
}
#[tokio::main]
async fn main() {
MyCredsProvider::new();
// Some multi-threaded / concurrent logic to periodically refresh creds
todo!()
}

How to assert two variables are equal when the implementation of PartialEq isn't appropriate?

In my program, I represent "events" with the following structure:
struct Event {
value: &'static str,
timestamp: usize,
}
So far, I used PartialEq to compare Event variables: most of the time, I consider two Events to be equal if their value is the same:
impl PartialEq for Event {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_loose_equality() {
let a = Event { value: "a-value", timestamp: 12345 };
let b = Event { value: "a-value", timestamp: 23456 };
assert_eq!(a, b);
}
}
However, in certain tests, I would like to ensure that two such variables are "strictly equals": The test should fail they have different timestamp (are not the same in respect to Eq).
As per the documentation of assert_eq!:
Asserts that two expressions are equal to each other (using PartialEq).
source
So, I am looking for an Eq equivalent, an assert_Eq_eq! in sort.
(or am I misunderstanding how Eq works and should be used?)
Here is what I fail to complete:
impl Eq for Event {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_strict_equality() {
let a = Event { value: "a-value", timestamp: 12345 };
let b = Event { value: "a-value", timestamp: 12345 };
// ???
}
}
You're swimming upstream fighting against the current. Go with the flow. Let PartialEq be strict equality and define a separate trait or method for loose equality.
#[derive(Eq, PartialEq)]
struct Event { .. }
impl Event {
fn loose_eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
#[test]
fn test_loose_equality() {
let a = Event { value: "a-value", timestamp: 12345 };
let b = Event { value: "a-value", timestamp: 23456 };
assert!(a.loose_eq(b));
}
I would create "views" of the type as appropriate:
struct Event {
value: &'static str,
timestamp: usize,
}
#[derive(Debug, PartialEq)]
struct Exact<'a> {
value: &'a &'static str,
timestamp: &'a usize,
}
impl Event {
fn exact(&self) -> Exact<'_> {
let Self { value, timestamp } = self;
Exact { value, timestamp }
}
}
fn demo(a: Event, b: Event) {
assert_eq!(a.exact(), b.exact());
}
Here I've chosen to take references to each of the fields to demonstrate the general case, but you don't need references for this specific example (&str and usize implement Copy and are small).
You could also choose to not implement PartialEq on the original type at all, and only perform comparisons through views:
assert_eq!(a.exact(), b.exact());
assert_eq!(a.loose(), b.loose());
If you need the strict equality only in tests, and your struct only has two fields, I'd simply compare these fields directly:
let a = Event { value: "a-value", timestamp: 12345 };
let b = Event { value: "a-value", timestamp: 12345 };
assert_eq!(a.value, b.value);
assert_eq!(a.timestamp, b.timestamp);
This looks like the easiest and most readable option to me.
Yes, you are misunderstanding something. Eq doesn't have any new methods over PartialEq. It's just an assertion that the implementation of PartialEq is reflexive (as well as the transitivity and symmetry assumed with PartialEq).
If you want to have a different "strict" equality, you could make your own trait (if you expect to use this a lot) or simply have a method fn strict_eq(&self, other: &Self) -> bool attached to Event.
Given such a method you could write a macro to use that method.
The full source for assert_eq! can be found here and could be easily adapted, but a (perhaps overly) simple version would be something like
macro_rules! assert_strict_eq {
($left: expr, $right: expr) => {
if !$left.strict_eq(&$right) {
panic!(r#"assertion failed: `(left == right)`
left: `{:?}`,
right: `{:?}`"#, $left, $right)
}
}
}
(example usage)
Unlike JavaScript, Rust does not support equal vs. strict equal.
However, you can achieve similar effect by implementing your own trait and macro:
pub trait StrictEq {
fn strict_eq(&self, other: &Self) -> bool;
}
macro_rules! assert_strict_eq {
($left:expr, $right:expr) => {{
match (&$left, &$right) {
(left_val, right_val) => {
assert!(left_val.strict_eq(right_val));
}
}
}};
}
The implementation for Event would be rather simple:
impl StrictEq for Event {
fn strict_eq(&self, other: &Self) -> bool {
self.value == other.value && self.timestamp == other.timestamp
}
}

How to write unit tests for asynchronous function with a callback argument

I'm writing common tests for my kotlin multiplatform library which implements the API business logic using ktor client library.
I have a function which takes a callback as an argument, use coroutines to make the request to the API, and then execute the callback.
Here is a simplified version of the function from my UserApi class I want to test
fun <T : Any> fetch(
requestBuilder: HttpRequestBuilder,
deserializer: DeserializationStrategy<T>,
callback: (Either<ErrorMessage, T>) -> Unit)
{
GlobalScope.launch(dispatcherIO) {
val result: Either<ErrorMessage, T> =
try {
val returnObject: T = Json.parse(
deserializer,
HttpClient().post(requestBuilder)
)
Either.Right(returnObject)
} catch (e: Exception) {
Either.Left(ErrorMessage(e.message))
}
withContext(dispatcherMain) { callback(result) }
}
}
I would like to write a unit test like that:
#Test
fun requestOK() {
runTest { //runTest returns a platform specific runBlocking
val UserApi().fetch(request, User.serializer()) {
it.fold(
{ failure -> fail("must return success" },
{ user -> assertEquals(expectedUser, user) }
)
}
}
}

How to check if a function has been called in Rust?

I have a function as follows
pub fn registration(student_id: &T::StudentId, registrar: &T::RegistrarID) {
// More code here.
if num_of_students < student_limit {
Self::function_one(&registrar, &num_of_students);
} else {
Self::function_two(&num_of_students);
}
}
In unit tests, I am planning to check whether function_one or function_two was called.
#[test]
fn registration_more_students_should_call_functon_one() {
with_test_data(
&mut TestBuilder::default().num_of_students(1000).build(),
|| {
//assert_called!(module_name::registration("TV:009", "DF-000-09"));
},
);
}
How can I test if a function was called in Rust?
Strong opinion alert: you are doing your testing wrong. This is on the same level as "how do I test a private method". You shouldn't care about the implementation of registration to this level of detail.
That being said, if it's actually important to know which if branch is taken, then use dependency injection:
fn registration(mut registration: impl Registration, registrar: i32) {
let num_of_students = 0;
let student_limit = 0;
if num_of_students < student_limit {
registration.function_one(registrar, num_of_students);
} else {
registration.function_two(num_of_students);
}
}
trait Registration {
fn function_one(&mut self, registrar: i32, num_of_students: i32);
fn function_two(&mut self, num_of_students: i32);
}
impl<R: Registration> Registration for &'_ mut R {
fn function_one(&mut self, registrar: i32, num_of_students: i32) {
(**self).function_one(registrar, num_of_students)
}
fn function_two(&mut self, num_of_students: i32) {
(**self).function_two(num_of_students)
}
}
/*
// An example implementation for production
struct DatabaseRegistration;
impl Registration for DatabaseRegistration {
fn function_one(&mut self, registrar: i32, num_of_students: i32) {
eprintln!("Do DB work: {}, {}", registrar, num_of_students)
}
fn function_two(&mut self, num_of_students: i32) {
eprintln!("Do DB work: {}", num_of_students)
}
}
*/
#[cfg(test)]
mod test {
use super::*;
#[derive(Debug, Copy, Clone, Default)]
struct TestRegistration {
calls_to_one: usize,
calls_to_two: usize,
}
impl Registration for TestRegistration {
fn function_one(&mut self, _: i32, _: i32) {
self.calls_to_one += 1;
}
fn function_two(&mut self, _: i32) {
self.calls_to_two += 1;
}
}
#[test]
fn calls_the_right_one() {
let mut reg = TestRegistration::default();
registration(&mut reg, 42);
assert_eq!(1, reg.calls_to_two)
}
}
Once you have done this, then you can see that registration calls the appropriate trait function (as shown in the example test).
This prevents your production code from having test-specific detritus strewn about while also giving you the ability to be more flexible and test more cases rapidly.
See also:
How can I test stdin and stdout?
How to mock external dependencies in tests?
Is there a cleaner way to test functions that use functions that require user input in Rust?
How can I test Rust methods that depend on environment variables?
Is there a way of detecting whether code is being called from tests in Rust?
Here is a naïve attempt using #[cfg(test)] in multiple places:
struct Registration {
students: Vec<String>,
#[cfg(test)]
function_1_called: bool,
}
impl Registration {
fn new() -> Self {
Registration {
students: Vec::new(),
#[cfg(test)]
function_1_called: false,
}
}
fn function_1(&mut self, students: Vec<String>) {
self.students.extend(students);
#[cfg(test)]
{
self.function_1_called = true;
}
}
fn function_2(&mut self, students: Vec<String>) {}
fn f(&mut self, students: Vec<String>) {
if students.len() < 100 {
self.function_1(students);
} else {
self.function_2(students);
}
}
}
fn main() {
println!("Hello, world!");
let r = Registration::new();
// won't compile during `run`:
// println!("{}", r.function_1_called);
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_f() {
let mut r = Registration::new();
r.function_1(vec!["Alice".to_string(), "Bob".to_string()]);
assert!(r.function_1_called);
}
}
The code is loosely based on the snippets that you provided. There is a Registration struct that holds a list of student names, two methods function_1 and function_2 for registering students, and a function f that chooses between function_1 and function_2 depending o how many students there are.
During tests, Registration is compiled with an additional Boolean variable function_1_called. This variable is set only if function_1 is called (the block of code that does that is also marked with #[cfg(test)]).
In combination, this makes an additional Boolean flag available during the tests, so that you can make assertions like the following one:
assert!(r.function_1_called);
Obviously, this could work for structures much more complicated than a single boolean flag (which does not at all mean that you should actually do it).
I cannot comment on whether this is idiomatic in Rust or not. The whole setup feels as if it should be hidden behind fancy macros, so if this style of testing is used in Rust at all, there should already be crates that provide those (or similar) macros.

How to write proper test for reactive interface repository which returns Observable only when there is some event, how to mock triggering that event

I am trying to write proper test for my reactive repository, which just listen for location changes. I don't want to actually listen for location changes and just 'invoke' new location change multiple times with just created custom Location.
Here is the repository and its function for gathering locations:
interface RxLocationRepository {
#SuppressLint("MissingPermission")
fun onLocationUpdate(): Observable<Location>
fun stopLocationUpdates()
}
Here is the implementation of it, which actually DON'T MATTER because i don't want to listen for real location updates, but i want to show you that it is just reactive implementation for listen to LocationManager updates:
class LocationNativeRepository(
val locationManager: LocationManager,
val geoEventsDistanceMeters: Int,
val geoEventsIntervalSeconds: Int) : RxLocationRepository{
var locationToPopulate: Location = Location(LocationManager.GPS_PROVIDER)
lateinit var mLocationCallbackNativeApi: LocationListener
private val subject: BehaviorSubject<Location> = BehaviorSubject.createDefault(locationToPopulate)
var locationEmitter: Observable<Location> = subject.hide()
init {
configureNativeLocationEmitter()
}
override fun onLocationUpdate(): Observable<Location> {
return locationEmitter
}
#SuppressLint("MissingPermission")
override fun stopLocationUpdates() {
locationManager.removeUpdates(mLocationCallbackNativeApi)
}
#SuppressLint("MissingPermission")
private fun configureNativeLocationEmitter() {
mLocationCallbackNativeApi = object : LocationListener {
override fun onLocationChanged(location: Location) {
subject.onNext(location)
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}
try {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
(geoEventsIntervalSeconds * 1000).toLong(),
geoEventsDistanceMeters.toFloat(),
mLocationCallbackNativeApi,
Looper.getMainLooper())
} catch (ignored: IllegalArgumentException) {
ignored.printStackTrace()
}
try {
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
(geoEventsIntervalSeconds * 1000).toLong(),
geoEventsDistanceMeters.toFloat(),
mLocationCallbackNativeApi,
Looper.getMainLooper())
} catch (ignored: IllegalArgumentException) {
ignored.printStackTrace()
}
}
}
So how i can invoke this repository in my test to actually trigger the onLocationUpdate() method? So for example i will make it emit 3 times location like this:
val location = Location("test").apply {
latitude = 1.234
longitude = 5.678
accuracy = 20f
time = Date().time
}
Create a PublishSubject<Location> variable. Create an anonymous sub-class of RxLocationRepository, where the onLocationUpdate() method returns the variable. Then emit values into the variable to provide new locations.