Skip to content

Commit

Permalink
feat(mock): add args
Browse files Browse the repository at this point in the history
  • Loading branch information
leroyguillaume committed Nov 20, 2023
1 parent 50dac94 commit ad85f14
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 15 deletions.
36 changes: 36 additions & 0 deletions examples/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use mockable::{DefaultEnv, Env};

fn main() {
let env = DefaultEnv;
let val_1 = env.string("SECRET_1");
let val_2 = env.string("SECRET_2");
println!("{val_1:?} {val_2:?}");
}

#[cfg(test)]
mod test {
use mockable::{Mock, MockEnv};

use super::*;

#[test]
fn test() {
let mock = Mock::with(vec![
Box::new(|key: String| {
assert_eq!(key, "SECRET_1");
Some("val_1".into())
}),
Box::new(|key: String| {
assert_eq!(key, "SECRET_2");
Some("val_2".into())
}),
]);
let mut env = MockEnv::new();
env.expect_string()
.returning(move |key| mock.call_with_args(key.into()));
let val_1 = env.string("SECRET_1");
let val_2 = env.string("SECRET_2");
assert_eq!(val_1, Some("val_1".into()));
assert_eq!(val_2, Some("val_2".into()));
}
}
52 changes: 37 additions & 15 deletions src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@ use std::sync::{
Arc,
};

type MockFn<RETURN, ARGS> = dyn Fn(ARGS) -> RETURN + Send + Sync;

/// Struct that represents a function mock.
///
/// **This is supported on `feature=mock` only.**
///
/// [Example](https://github.com/leroyguillaume/mockable/tree/main/examples/mock.rs).
pub struct Mock<E> {
pub struct Mock<RETURN, ARGS = ()> {
idx: Arc<AtomicUsize>,
kind: MockKind<E>,
kind: MockKind<RETURN, ARGS>,
}

impl<E> Mock<E> {
impl<RETURN, ARGS> Mock<RETURN, ARGS> {
/// Creates a new `Mock` that always returns always the same result.
pub fn always<F: Fn(usize) -> E + Send + Sync + 'static>(f: F) -> Self {
pub fn always_with_args<F: Fn(usize, ARGS) -> RETURN + Send + Sync + 'static>(f: F) -> Self {
Self {
idx: Arc::new(AtomicUsize::new(0)),
kind: MockKind::Always(Arc::new(Box::new(f))),
Expand All @@ -28,12 +30,12 @@ impl<E> Mock<E> {
}

/// Creates a new `Mock` that should be called only once.
pub fn once<F: Fn() -> E + Send + Sync + 'static>(f: F) -> Self {
pub fn once_with_args<F: Fn(ARGS) -> RETURN + Send + Sync + 'static>(f: F) -> Self {
Self::with(vec![Box::new(f)])
}

/// Creates a new `Mock` that should be called several times.
pub fn with(f: Vec<Box<dyn Fn() -> E + Send + Sync>>) -> Self {
pub fn with(f: Vec<Box<dyn Fn(ARGS) -> RETURN + Send + Sync>>) -> Self {
Self {
idx: Arc::new(AtomicUsize::new(0)),
kind: MockKind::CallSpecific(Arc::new(f)),
Expand All @@ -44,15 +46,15 @@ impl<E> Mock<E> {
///
/// # Panics
/// Panics if the mock has been called more times than expected.
pub fn call(&self) -> E {
pub fn call_with_args(&self, args: ARGS) -> RETURN {
let idx = self.idx.fetch_add(1, Ordering::Relaxed);
match &self.kind {
MockKind::Always(f) => f(idx),
MockKind::Always(f) => f(idx, args),
MockKind::CallSpecific(fns) => {
if idx >= fns.len() {
panic!("Mock called when it should not have been");
}
fns[idx]()
fns[idx](args)
}
}
}
Expand All @@ -73,7 +75,27 @@ impl<E> Mock<E> {
}
}

impl<E> Clone for Mock<E> {
impl<RETURN> Mock<RETURN, ()> {
/// Creates a new `Mock` that always returns always the same result.
pub fn always<F: Fn(usize) -> RETURN + Send + Sync + 'static>(f: F) -> Self {
Self::always_with_args(move |idx, _| f(idx))
}

/// Creates a new `Mock` that should be called only once.
pub fn once<F: Fn() -> RETURN + Send + Sync + 'static>(f: F) -> Self {
Self::once_with_args(move |_| f())
}

/// Returns the result of the mock.
///
/// # Panics
/// Panics if the mock has been called more times than expected.
pub fn call(&self) -> RETURN {
self.call_with_args(())
}
}

impl<RETURN, ARGS> Clone for Mock<RETURN, ARGS> {
fn clone(&self) -> Self {
Self {
idx: self.idx.clone(),
Expand All @@ -82,20 +104,20 @@ impl<E> Clone for Mock<E> {
}
}

impl<E> Default for Mock<E> {
impl<RETURN, ARGS> Default for Mock<RETURN, ARGS> {
fn default() -> Self {
Self::never()
}
}

// MockKind

enum MockKind<E> {
Always(Arc<Box<dyn Fn(usize) -> E + Send + Sync>>),
CallSpecific(Arc<Vec<Box<dyn Fn() -> E + Send + Sync>>>),
enum MockKind<RETURN, ARGS> {
Always(Arc<Box<dyn Fn(usize, ARGS) -> RETURN + Send + Sync>>),
CallSpecific(Arc<Vec<Box<MockFn<RETURN, ARGS>>>>),
}

impl<E> Clone for MockKind<E> {
impl<RETURN, ARGS> Clone for MockKind<RETURN, ARGS> {
fn clone(&self) -> Self {
match self {
MockKind::Always(f) => MockKind::Always(f.clone()),
Expand Down

0 comments on commit ad85f14

Please sign in to comment.