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 animplblock 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, andSyncderive/auto traits.
faux also provides easy-to-use argument matchers.
Modules§
- matcher
- Tools to check if an argument to a mocked method matches expectations.
- when
- Tools to stub the implementation or return value of your mocks.
Macros§
- from_fn
- Returns an
ArgMatcherthat succeeds if the provided closure returnstrue. - pattern
- Returns an
ArgMatcherthat succeeds if the provided pattern matches. - when
- Creates a
Wheninstance to stub a specific method in a struct.
Structs§
- Invocation
Error - When
- Provides methods to stub the implementation or return value of the stubbed method.
Traits§
- ArgMatcher
- Matcher for single argument of a method.