faux

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.
See the API docs for more information.
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:
[]
= "^0.1"
faux provides two attributes:
#[create]: transforms a struct into a mockable equivalent#[methods]: transforms the methods in animplblock into
Use Rust's #[cfg_attr(...)] to gate these attributes to the test
config only.
Examples
use crateUserClient;
// A sample #[test] for Service that mocks the client::UserClient
Due to constraints with rustdocs, the above example tests in
main() rather than a #[test] function. In real life, the faux
attributes should be gated to #[cfg(test)].
Features
faux lets you mock 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 (but not external crates).
faux also provides easy-to-use argument matchers.
Interactions With Other Proc Macros
While faux makes no guarantees that it will work with other macro
libraries, it should "just" work. There are some caveats, however. For
a quick solution, try making the faux attributes (e.g.
#[faux::methods]) the first attribute.
Explanation
If another proc-macro modifies the signature of a method before
faux does its macro expansion, then it could modify the signature
into something not supported by faux. Unfortunately, the order of
proc macros is not specified. However, in practice it seems to
expand top-down (tested in Rust 1.42).
In the snippet above, #[faux::methods] will expand first followed by
#[another_attribute].faux is effectively ignoring the other macro
and expanding based on the code you wrote.
If #[faux::methods] performs its expansion after another macro has
modified the impl block, #[faux::methods] receives the expanded
code. This code might contain different method signatures than what
you originally wrote. Note that the other proc macro's expansion may
create code that faux cannot handle (e.g. explicit lifetimes).
For a concrete example, let's look at
async-trait. async-trait effectively converts:
async
Because async-trait adds explicit lifetimes to the method signature,
which faux cannot handle, having async-trait do its expansion
first breaks faux. Note that even if faux could handle explicit
lifetimes, our signature is now so unwieldy that it would make mocks
hard to work with. Because async-trait just wants an async
function signature, and faux does not modify function signatures, it
is okay for faux to expand first.
If you find a proc macro that faux cannot handle, please open an
issue to see if faux is doing something unexpected that conflicts
with that macro.
Goal
faux was founded on the belief that traits with single
implementations are an undue burden and an unnecessary layer of
abstraction. Thus, faux does not rely on trait definitions for every
mocked object, which would pollute their function signatures with
either generics or trait objects. faux aims to create mocks out of
user-defined structs, avoiding extra production code that exists
solely for tests.
Inspiration
This library was inspired by mocktopus, a mocking library for
nightly Rust that lets you mock any function. Unlike mocktopus, faux
works on stable Rust and deliberately only allows for mocking public
methods in structs.