Skip to content
Permalink
Browse files

Merge pull request #173 from myelin-ai/returns-once

Implement one time return values
  • Loading branch information...
bash committed Jun 7, 2019
2 parents 73a2334 + abc0359 commit bfd8d83a64d41d26ef593b304c183fb7c4ba61be
Showing with 175 additions and 0 deletions.
  1. +31 −0 src/method_call.rs
  2. +6 −0 src/return_value.rs
  3. +86 −0 src/return_value/once.rs
  4. +52 −0 tests/returns_once.rs
@@ -44,6 +44,19 @@ where
self
}

/// Defines a return value for this method that will be returned once.
/// The mocked method will panic on subsequent calls.
///
/// This method does not need to be called on nightly for the unit type `()`.
pub fn returns_once(&mut self, return_value: R) -> &mut Self
where
R: 'mock,
{
self.call.return_value = Some(Rc::new(return_value::Once::new(return_value)));
self.assert_times_and_return_value_are_compatible();
self
}

/// Defines that this method panics.
pub fn panics(&mut self) -> &mut Self {
self.call.return_value = Some(Rc::new(return_value::Panic(None)));
@@ -81,12 +94,30 @@ where
E: Into<ExpectedCalls>,
{
self.call.expected_calls = expected_calls.into();
self.assert_times_and_return_value_are_compatible();
self
}

pub(crate) fn new(call: &'a mut MethodCall<'mock, A, R>) -> Self {
Self { call }
}

fn assert_times_and_return_value_are_compatible(&self) {
let one_expected_call = ExpectedCalls::from(1);
let returns_only_once = self
.call
.return_value
.as_ref()
.map(|r| !r.can_return_more_than_once())
.unwrap_or_default();
let expected_calls = &self.call.expected_calls;
if returns_only_once && expected_calls != &one_expected_call {
panic!(
"Return value can only be returned once but call was expected {}.",
expected_calls
);
}
}
}

pub(crate) struct MethodCall<'mock, A, R> {
@@ -1,11 +1,13 @@
pub(crate) use self::cloned::*;
pub(crate) use self::once::*;
pub(crate) use self::panic::*;

use crate::matcher::ArgumentsMatcher;
use std::fmt::{Debug, Display};
use std::rc::Rc;

mod cloned;
mod once;
mod panic;

pub(crate) trait DefaultReturnValue<A>: Sized {
@@ -31,4 +33,8 @@ where
A: for<'args> ArgumentsMatcher<'args>,
{
fn generate_return_value(&self, input: <A as ArgumentsMatcher<'_>>::Arguments) -> R;

fn can_return_more_than_once(&self) -> bool {
true
}
}
@@ -0,0 +1,86 @@
use super::ReturnValueGenerator;
use crate::fmt::MaybeDebug;
use crate::matcher::ArgumentsMatcher;
use std::cell::RefCell;
use std::fmt::{self, Debug, Display};

pub(crate) struct Once<T>(RefCell<Option<T>>);

impl<T> Once<T> {
pub(crate) fn new(value: T) -> Self {
Self(RefCell::new(Some(value)))
}
}

impl<A, R> ReturnValueGenerator<A, R> for Once<R>
where
A: for<'args> ArgumentsMatcher<'args>,
{
fn generate_return_value(&self, _: <A as ArgumentsMatcher<'_>>::Arguments) -> R {
self.0
.borrow_mut()
.take()
.expect("This value was already returned")
}

fn can_return_more_than_once(&self) -> bool {
false
}
}

impl<R> Display for Once<R>
where
R: MaybeDebug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
MaybeDebug::fmt(&self.0, f)
}
}

impl<R> Debug for Once<R>
where
R: MaybeDebug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
MaybeDebug::fmt(&self.0, f)
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::arguments::ArgumentsMock;
use crate::matcher::ArgumentsMatcherMock;

#[test]
fn returns_expected_value() {
let return_value = Once::new(String::from("foo"));

assert_eq!(
String::from("foo"),
ReturnValueGenerator::<ArgumentsMatcherMock, String>::generate_return_value(
&return_value,
ArgumentsMock
)
);
}

#[test]
#[should_panic]
fn panics_when_called_more_than_once() {
let return_value = Once::new(String::from("foo"));

assert_eq!(
String::from("foo"),
ReturnValueGenerator::<ArgumentsMatcherMock, _>::generate_return_value(
&return_value,
ArgumentsMock
)
);

ReturnValueGenerator::<ArgumentsMatcherMock, _>::generate_return_value(
&return_value,
ArgumentsMock,
);
}
}
@@ -0,0 +1,52 @@
use mockiato::mockable;

#[derive(Debug)]
struct Message;

#[cfg_attr(test, mockable)]
trait MessageGenerator {
fn generate_message(&self) -> Message;
}

#[test]
fn mocked_method_returns_expected_value() {
let message_generator = message_generator_mock();
let _ = message_generator.generate_message();
}

#[test]
#[should_panic]
fn mocked_method_panics_when_invoked_more_than_once() {
let message_generator = message_generator_mock();
let _ = message_generator.generate_message();
let _ = message_generator.generate_message();
}

#[test]
#[should_panic]
fn setup_of_method_panics_when_times_is_specified_after_return_value() {
let mut message_generator = MessageGeneratorMock::new();
message_generator
.expect_generate_message()
.returns_once(Message)
.times(2);
}

#[test]
#[should_panic]
fn setup_of_method_panics_when_times_is_specified_before_return_value() {
let mut message_generator = MessageGeneratorMock::new();
message_generator
.expect_generate_message()
.times(2)
.returns_once(Message);
}

fn message_generator_mock() -> Box<dyn MessageGenerator> {
let mut message_generator = MessageGeneratorMock::new();
message_generator.expect_generate_message_calls_in_order();
message_generator
.expect_generate_message()
.returns_once(Message);
Box::new(message_generator)
}

0 comments on commit bfd8d83

Please sign in to comment.
You can’t perform that action at this time.