faux

faux is a traitless mocking library for stable Rust. It was inspired by mocktopus, a mocking library for nightly Rust that lets you mock any function. Unlike mocktopus, faux deliberately only allows for mocking public methods in structs.
See the API docs for more information.
faux is in its early alpha stages, so there are no guarantees of API stability.
Setup
faux will modify existing code at compile time to transform structs
and their methods into mockable versions of themselves. faux makes
liberal use of unsafe Rust features, so it is only recommended for use
inside of tests. Add faux as a dev-dependency in Cargo.tomlto
prevent usage in production code:
[]
= "0.0.8"
faux provides two attributes: create and methods. Use these
attributes for tagging your struct and its impl block
respectively. Use Rust's #[cfg_attr(...)] to gate these attributes
to the test config only.
Usage
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)].
Interactions With Other Proc Macros
faux makes no guarantees that it will work with other macro
libraries. faux in theory should "just" work although with some
caveats, in particular if they modify the signature of
methods.
Unfortunately, the order of proc macros is not specified. However, in practive it seems to expand top-down (tested in Rust 1.42).
In the snippet above, #[faux::methods] will expand first followed by
#[another_attribute].
If faux does its expansion first then faux will effectively ignore
the other macro and expand based on the code that the user wrote. If
you want faux to treat the code in the impl block (or the
struct) as-is, before the expansion then put it on the top.
If faux does its expansion after, then faux will morph the
expanded version of the code, which might have a different signature
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 modifies the signature of the function to a
signature that faux cannot handle (explicit lifetimes) then having
async-trait do its expansion before faux would make faux not
work. Note that even if faux could handle explicit lifetimes, our
signature now it's 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.
Since no expansions came before, faux sees an async function,
which it supports. faux does its magic assuming this is a normal
async function, and then async-trait does its magic to convert the
signature to something that can work on trait impls.
If you find a procedural macro that faux cannot handle please submit
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. It aims to create mocks out of user-defined structs, avoiding extra production code that exists solely for tests. In particular, faux does not rely on trait definitions for every mocked object, which would pollute their function signatures with either generics or trait objects.