Expand description
A library to create mocks out of structs.
faux
allows you to mock the methods of structs for testing
without complicating or polluting your code.
Part of faux
’s philosophy is that only visible behavior should
be mocked. In practice, this means faux
only mocks public
methods. Fields are not mocked, as they are data, not
behavior. Private methods are not mocked, as they are invisible to
others.
At a high level, faux
is split into:
#[create]
: transforms a struct into a mockable equivalent#[methods]
: transforms the methods in animpl
block into their mockable equivalentswhen!
: initializes a method stub by returning aWhen
. Passing optional argument matchers restricts which arguments will invoke the stub.When
: lets you stub a method’s return value or implementation
§Getting Started
faux
makes liberal use of unsafe Rust features, so it is only
recommended for use inside tests. To prevent faux
from leaking
into your production code, set it as a dev-dependency
in your
Cargo.toml
:
[dev-dependencies]
faux = "^0.1"
§Examples
§Simple
// restrict faux to tests by using `#[cfg_attr(test, ...)]`
// faux::create makes a struct mockable and generates an
// associated `faux()` function
// e.g.: `HttpClient::faux()` will create a mock `HttpClient` instance
#[cfg_attr(test, faux::create)]
pub struct HttpClient { /* */ }
// this is just a bag of data with no behavior
// so we do not attach `#[faux::create]`
#[derive(PartialEq, Clone, Debug)]
pub struct Headers {
pub authorization: String,
}
// `faux::methods` makes every public method in the `impl` block mockable
#[cfg_attr(test, faux::methods)]
impl HttpClient {
pub fn post(&self, path: &str, headers: &Headers) -> String {
/* makes network calls that we'd rather not do in unit tests */
}
}
#[cfg(test)]
#[test]
fn test() {
// use the generated `faux()` function to create a mock instance
let mut mock = HttpClient::faux();
let headers = Headers { authorization: "Bearer foobar".to_string() };
// use `faux::when!` to stub the behavior of your methods
// you can specify arguments to match against when the stub is invoked
faux::when!(
// arguments are converted into argument matchers
// the default argument matcher performs an equality check
// use `_` to create a universal argument matcher
// the argument matchers below specify to ignore the first argument
// but that the second one must equal `headers`
mock.post(_, headers.clone())
)
// stub the return value
.then_return("{}".to_string());
assert_eq!(mock.post("any/path/does/not/mater", &headers), "{}");
assert_eq!(mock.post("as/i/said/does/not/matter", &headers), "{}");
// if you want to stub all calls to a method, you can omit argument matchers
faux::when!(mock.post).then_return("OK".to_string());
let other_headers = Headers { authorization: "other-token".to_string() };
assert_eq!(mock.post("other/path", &other_headers), "OK");
}
§Stubbing the same method multiple times
A single method can be stubbed multiple times. When doing so,
faux
checks every stub for the method in a last-in-first-out
fashion until it finds a stub whose argument matchers match the
invocation arguments.
#[cfg(test)]
#[test]
fn test() {
let mut mock = HttpClient::faux();
let headers = Headers { authorization: "Bearer foobar".to_string() };
let other_headers = Headers { authorization: "other-token".to_string() };
// catch-all stub to return "OK"
faux::when!(mock.post).then_return("OK".to_string());
// stub for specific headers to return "{}"
faux::when!(mock.post(_, headers.clone())).then_return("{}".to_string());
assert_eq!(mock.post("some/path", &headers), "{}"); // matches specific stub
assert_eq!(mock.post("some/path", &other_headers), "OK"); // matches catch-all stub
}
§Stubbing implementation
faux
supports stubbing of not just the return value but also the
implementation of a method. This is done using then()
.
#[cfg(test)]
#[test]
fn test() {
let mut mock = HttpClient::faux();
let headers = Headers { authorization: "Bearer foobar".to_string() };
faux::when!(mock.post).then(|(path, _)| path.to_string().to_uppercase());
assert_eq!(mock.post("another/path", &headers), "ANOTHER/PATH");
}
§Stubbing with non-static data
Let’s add a new method to our HttpClient
that returns borrowed
data. This cannot be stubbed using safe code, so faux
provides
.then_unchecked()
and .then_unchecked_return()
to stub such
methods.
#[cfg_attr(test, faux::methods)]
impl HttpClient {
pub fn host(&self) -> &str {
/* returns a reference to some internal data */
}
}
#[cfg(test)]
#[test]
fn test() {
let mut mock = HttpClient::faux();
// `then_unchecked()` and `then_unchecked_return()` require unsafe
// they allow stubbing methods that return non-static values (e.g. references)
// or to stub using non-static closures
let ret = "some-value".to_string();
unsafe { faux::when!(mock.host).then_unchecked_return(ret.as_str()) }
assert_eq!(mock.host(), &ret);
}
§Features
faux
lets you stub the return value or implementation of:
- Async methods
- Trait methods
- Generic struct methods
- Methods with pointer self types (e.g.,
self: Rc<Self>
) - Methods in external modules
- Support for
Debug
,Default
,Clone
,Send
, andSync
derive/auto traits.
faux
also provides easy-to-use argument matchers.
Modules§
- Tools to check if an argument to a mocked method matches expectations.
- Tools to stub the implementation or return value of your mocks.
Macros§
- Returns an
ArgMatcher
that succeeds if the provided closure returnstrue
. - Returns an
ArgMatcher
that succeeds if the provided pattern matches. - Creates a
When
instance to stub a specific method in a struct.
Structs§
- Provides methods to stub the implementation or return value of the stubbed method.
Traits§
- Matcher for single argument of a method.
Attribute Macros§
- Transforms a struct into a mockable version of itself.
- Transforms methods in an
impl
block into mockable versions of themselves.