Struct mocking library for Rust

Overview

faux   Latest Version rustc 1.45+ docs

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:

[dev-dependencies]
faux = "^0.1"

faux provides two attributes:

  • #[create]: transforms a struct into a mockable equivalent
  • #[methods]: transforms the methods in an impl block into

Use Rust's #[cfg_attr(...)] to gate these attributes to the test config only.

#[cfg_attr(test, faux::create)]
pub struct MyStructToMock { /* fields */ }

#[cfg_attr(test, faux::methods)]
impl MyStructToMock { /* methods to mock */ }

Examples

mod client {
    // #[faux::create] makes a struct mockable and
    // generates an associated `faux` function
    // e.g., `UserClient::faux()` will create a a mock `UserClient` instance
    #[faux::create]
    pub struct UserClient { /* data of the client */ }

    #[derive(Clone)]
    pub struct User {
        pub name: String
    }

    // #[faux::methods ] makes every public method in the `impl` block mockable
    #[faux::methods]
    impl UserClient {
        pub fn fetch(&self, id: usize) -> User {
            // does some network calls that we rather not do in tests
            User { name: "".into() }
        }
    }
}

use crate::client::UserClient;

pub struct Service {
    client: UserClient,
}

#[derive(Debug, PartialEq)]
pub struct UserData {
    pub id: usize,
    pub name: String,
}

impl Service {
    fn user_data(&self) -> UserData {
        let id = 3;
        let user = self.client.fetch(id);
        UserData { id, name: user.name }
    }
}

// A sample #[test] for Service that mocks the client::UserClient
fn main() {
    // create a mock of client::UserClient using `faux`
    let mut client = client::UserClient::faux();

    // mock fetch but only if the argument is 3
    // argument matchers are optional
    faux::when!(client.fetch(3))
        // stub the return value for this mock
        .then_return(client::User { name: "my user name".into() });

    // prepare the subject for your test using the mocked client
    let subject = Service { client };

    // assert that your subject returns the expected data
    let expected = UserData { id: 3, name: String::from("my user name") };
    assert_eq!(subject.user_data(), expected);
}

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).

#[faux::create]
struct Foo { /*some items here */ }

#[faux::methods]
#[another_attribute]
impl Foo {
    /* some methods here */
}

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 fn run(&self, arg: Arg) -> Out {
    /* stuff inside */
}
fn run<'async>(&'async self, arg: Arg) -> Pin<Box<dyn std::future::Future<Output = Out> + Send + 'async>> {
    /* crazier stuff inside */
}

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.

#[faux::methods]
#[async_trait]
impl MyStruct for MyTrait {
    async fn run(&self, arg: Arg) -> Out {
        /* stuff inside */
    }
}

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.

Comments
  • Support self: Rc

    Support self: Rc

    Thanks for this really cool library!

    I can't get this to work:

    #[faux::methods]
    impl Struct {
        pub fn foo(self: Rc<Self>) {
           ...
        }
    }
    
    opened by audunhalland 10
  • Implement Clone for MockStore

    Implement Clone for MockStore

    In one of my projects I need to clone a struct that might be mocked. Doing this in a test currently results in a panic. This PR implements Clone on the MockStore, which allows us to remove the panic on a clone of MaybeFaux::Faux. I have tested it locally and it works in my codebase, but if these changes have any undesired behaviour in certain conditions please let me know and I might be able to fix it. Alternatively, I could wrap the Mutex in an Arc, which would make it cheaper to clone the store.

    opened by orbwalk 9
  • Multiple calls mocks.

    Multiple calls mocks.

    Currently when we set up a mock:

    unsafe { when!(my_mock.some_method).then(|_| { /* do things */} )
    

    It sets up a one time mock, which means that after the mock is invoked, next time it runs the code will panic saying that no mock was set.

    This library should support mocks that are set up once and run multiple times, either a set number of times, or forever.

    opened by nrxus 9
  • Derived/Auto traits on mocks

    Derived/Auto traits on mocks

    What traits should the mocks support deriving?

    aa18c66a349c8f33ad061e2d6ff655233aa2b762 added support for Sync/Send/Debug.

    What about Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash, Default?

    opened by nrxus 8
  • How to mock stuff in external crates?

    How to mock stuff in external crates?

    Hey, cool crate! I'd really like to use it to mock methods in an external crate's struct but I can't really find an example on that. Basically, something like

    #[faux::create]
    use SomeOtherCrate::SomeStruct;
    

    Is this currently possible?

    opened by svenstaro 7
  • `faux::methods` panicked, « return type should not be Self »

    `faux::methods` panicked, « return type should not be Self »

    Hello, I wanted to use get rids of our custom mocker in meilisearch in favour of faux, thus I tried it with some of our simplest modules, but it's constantly panicking I don't understand what's going on. I extracted one of our simpler modules in another repo.

    Do you know what I'm doing wrong here? https://github.com/irevoire/faux-bug (you should run cargo test to make the panic appear)

    opened by irevoire 6
  • Mocks panic when accessed from multiple threads

    Mocks panic when accessed from multiple threads

    On 0.0.8, this minimal example panics:

    #[faux::create]
    struct Foo { }
    
    #[faux::methods]
    impl Foo {
        pub fn foo(&self) {}
    }
    
    fn main() {
        let mut fake = Foo::faux();
        faux::when!(fake.foo).then(|()| {});
        let fa1 = std::sync::Arc::new(fake);
        let fa2 = fa1.clone();
        let t1 = std::thread::spawn(move || loop { fa1.foo() });
        let t2 = std::thread::spawn(move || loop { fa2.foo() });
        t1.join().unwrap();
        t2.join().unwrap();
    }
    

    try_lock() in morphed.rs seems to be the cause.

    opened by Wesmania 6
  • Doesn't work correctly with async_trait

    Doesn't work correctly with async_trait

    I'm implementing a Clean Architecture, where I've got a number of Traits that represent my Use Cases, and then a single Struct that implements these. That works great, and means that I've got good structure to my code.

    However, testing it has been awkward in Rust. Faux seems to be ideal because I can mock out the structs and not need to have generics everywhere, but it doesn't seem to work with async_trait. By the errors, I'm needing to return a Pin<Box<Future<Output = XXX>> from my mock instead of just an XXX, but actually doing that is not always trivial.

    opened by sazzer 6
  • Rust-analyser shows argument names like `_x0`, `_x1` once mocked with Faux

    Rust-analyser shows argument names like `_x0`, `_x1` once mocked with Faux

    Hello,

    I experience that when a struct is mocked with Faux, Rust-analyzer shows the argument names of the struct's method like _x0, _x1 etc.

    Obviously, this is not a major issue, just wondering whether this is a common issue. Also, not sure if this is caused by Faux or by Rust-analyzer itself.

    Let me give an example to make my point more clear:

    #[cfg_attr(any(test, feature = "faux"), faux::create)]
    pub struct Client {
        pub n: u8,
    }
    
    #[cfg_attr(any(test, feature = "faux"), faux::methods)]
    impl Client {
        pub fn new() -> Self {
            return Client { n: 5 };
        }
    
        pub fn set_n(&mut self, n: u8, a: u8) {
            self.n = n;
        }
    }
    
    struct Foo {}
    
    impl Foo {
        pub fn new() -> Foo {
            let mut client = Client::new();
            client.set_n(5, 5);
            return Foo {};
        }
    }
    

    In this case, Rust-analyzer (in VSCode) shows the following:

    Screenshot 2022-05-28 at 10 08 44

    Note that I am not even using Client::faux() but just Client::new()

    When I remove the faux mocking around Client, it shows again n and a argument names.

    opened by nickdecooman 5
  • Faux pollutes implementation blocks that are not mocked

    Faux pollutes implementation blocks that are not mocked

    Not sure if if I'm using this crate ok, but given something like this:

    #[cfg_attr(test, faux::create)]
    pub struct MyStruct {
        pub something: i32,
    }
    
    impl MyStruct {
        pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
            Ok(Self { something: 3 })
        }
    }
    

    I get the error struct `subscriber::MyStruct` has no field named `something` field does not exist in the unmocked MyStruct::new function

    If I try to mock the impl too like:

    #[cfg_attr(test, faux::methods)]
    impl MyStruct {
        pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
            Ok(Self { something: 3 })
        }
    }
    

    I get the error

    mismatched types
    expected enum `std::result::Result<mymodule::MyStruct, _>`
       found enum `std::result::Result<mymodule::_FauxOriginal_MyStruct, _>`
    

    in the faux::methods proc attribute

    opened by cortopy 5
  • borrowed value does not live long enough

    borrowed value does not live long enough

    Looking into this mocking library, still relatively new to rust. Having some issues when trying to mock some object that I have that communicates with a unix socket.

    Creating a mock function and this is the following code in my test file.

            let mut conn = Connection::faux();
    
            faux::when!(conn.run_command).safe_then(|_| Ok(()));
    

    this is the function that is being mocked.

    #[faux::create]
    #[derive(Debug)]
    pub struct Connection {
        stream: TcpStream,
    }
    #[faux::methods]
    impl Connection {
        pub fn run_command(&mut self, command: &str) -> Result<()> {
        // read write
        }
    }
    

    this is the error when I try and compile the code.

    error[E0597]: `conn` does not live long enough
      --> tests/client.rs:9:21
       |
    4  | |
       | |__________________________________________________- argument requires that `conn` is borrowed for `'static`
    ...
    9  |           faux::when!(conn.run_command).safe_then(|_| Ok(()));
       |                       -^^^
       |                       |
       |  _____________________borrowed value does not live long enough
       | |
    ...
    13 |       }
       |       - `conn` dropped here while still borrowed
    
    error: aborting due to previous error
    

    Slightly confused about this error, because of the functions being called normally before trying out this library.

    Thanks for the library, just need some explanation on how to fix this code.

    opened by wcampbell0x2a 5
  • bug on trying to access to a field but rust-analyzer fails.

    bug on trying to access to a field but rust-analyzer fails.

    Hi! I have a struct with the faux::create attribute and the faux::methods in its implementation blocks, but when I try to access the struct's fields, VSCode in this case, it's not displaying the available fields. It only shows the field 0 and the struct methods.

    Despite this, the code runs and compiles well. But I'm not sure why, is there any runtime cost when using faux?

    the example below:

    Here it's the struct and then the VSCode screenshot:

    #[cfg_attr(test, faux::create)]
    #[derive(Clone)]
    pub struct DatabaseComponent {
        db_host: String,
        db_user: String,
        db_password: String,
        db_name: String,
        db_connection: Arc<Option<DBConnection>>,
        pub db_repos: Option<DBRepositories>,
    }
    
    #[cfg_attr(test, faux::methods)]
    impl DatabaseComponent {
        pub fn new(db_config: &DatabaseConfig) -> Self {
            Self {
                db_host: db_config.host.clone(),
                db_user: db_config.user.clone(),
                db_password: db_config.password.clone(),
                db_name: db_config.name.clone(),
                db_connection: Arc::new(None),
                db_repos: None,
            }
        }
    
        pub async fn run(&mut self) -> Result<(), sqlx::Error> {
           //.... not important stuff
        }
    }
    
    

    Data<AppComponents> it's an Arc that has AppComponents which owns the DatabaseComponent struct, and its unique pub field doesn't appear when u trying to access it.

    Screen Shot 2022-12-16 at 12 42 52

    Hope u can help me out and have a workaround to have a better developer experience.

    opened by lauti7 5
  • `faux::methods` bug on recognizing types

    `faux::methods` bug on recognizing types

    Hi! Ive been implementing faux to mock some structs but I'm stuck in an error that i can not work it out. Hope you can help me out with this. The issue is that the compiler is throwing an error due to the return type. The error is below:

    the return type refers to the mocked struct in a way that faux cannot handle. Split this function into animplblock not marked by #[faux::methods]. If you believe this is a mistake or it's a case that should be handled by faux please file an issue

    Here below is the code. The issue is on Redis::run function but this function doesn't return a type that refers to the mocked struct, it just returns ():

    #[cfg_attr(test, faux::create)]
    #[derive(Clone)]
    pub struct Redis {
        redis_host: String,
        pub pool: Option<Pool>,
    }
    
    impl std::fmt::Debug for Redis {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            f.debug_struct("Redis")
                .field("redis_host", &self.redis_host)
                .field("pool", &self.pool.is_some())
                .finish()
        }
    }
    
    #[cfg_attr(test, faux::methods)]
    impl Redis {
        pub fn new(config: &RedisConfig) -> Self {
            Self {
                redis_host: config.host.clone(),
                pool: None,
            }
        }
        async fn stop(&mut self) {
            self.pool.as_mut().unwrap().close()
        }
    
        async fn run(&mut self) -> Result<(), RedisError> {
            if self.pool.is_none() {
                let url = format!("redis://{}", self.redis_host);
                log::debug!("Redis URL: {}", url);
    
                match Config::from_url(url).create_pool(Some(Runtime::Tokio1)) {
                    Ok(pool) => {
                        self.pool = Some(pool);
                    }
                    Err(err) => {
                        log::debug!("Error on connecting to redis: {:?}", err);
                        panic!("Unable to connect to redis {:?}", err)
                    }
                };
    
                Ok("".to_string())
            } else {
                log::debug!("Redis Connection is already set.");
                Ok("".to_string())
            }
        }
    
        async fn get_async_connection(&mut self) -> Option<Connection> {
            match self.pool.as_mut().unwrap().get().await {
                Ok(connection) => Some(connection),
                Err(err) => {
                    log::error!("Error getting connection from redis: {:?}", err);
                    None
                }
            }
        }
    }
    

    That's all, hope you can help me.

    opened by lauti7 3
  • More matchers

    More matchers

    The only implemented matchers at the moment are:

    Any: matches any argument Eq matches based on Arg: PartialEq EqAgainst matches based on Arg: PartialEq<Against>

    We could implement other matchers so faux users don't have to handwrite their own matchers.

    Quick ideas:

    • <
    • <=
    • >
    • >=
    • !
    • ||
    • &&
    • contains
    • ~custom closure. Unsure if this is all that much better than a just implementing ArgMatcher though.~

    We can first decide what matchers to create and then see if when! can come up with an easy to use syntax for it.

    cc: @muscovite

    opened by nrxus 6
  • Capture slots

    Capture slots

    Add an argument matcher that is able to capture an argument for later verification.

    Potential pitfalls:

    • It will probably require the argument to be cloneable + 'static. This is to help prevent the argument being dropped before the slot.
    • It will probably require the slot to be surrounded by some sort of Rc<RefCell<T>>. This will help prevent any soundness issues with the slot being dropped before the last call to the mocked method.

    cc: @muscovite

    opened by nrxus 0
  • Mocking traits

    Mocking traits

    While there exist other libraries already capable of doing this, it might be nice to also provide that functionality here to not force the user to find yet another mocking framework on top of faux.

    opened by nrxus 1
HTTP mocking for Rust!

HTTP mocking for Rust! Get it on crates.io

Florin Lipan 407 Dec 31, 2022
Playwright is a rust library to automate Chromium, Firefox and WebKit built on top of Node.js library.

?? Playwright for Rust Playwright is a rust library to automate Chromium, Firefox and WebKit built on top of Node.js library. Installation [dependenci

octaltree 132 Jan 6, 2023
A library for generating fake data in Rust.

Fake A Rust library for generating fake data. Installation Default (rand is required): [dependencies] fake = "2.4" rand = "0.8" If you want to use #[d

cksac 552 Dec 25, 2022
Advanced Fuzzing Library - Slot your Fuzzer together in Rust! Scales across cores and machines. For Windows, Android, MacOS, Linux, no_std, ...

LibAFL, the fuzzer library. Advanced Fuzzing Library - Slot your own fuzzers together and extend their features using Rust. LibAFL is written and main

Advanced Fuzzing League ++ 1.2k Dec 29, 2022
QuickCheck bug hunting in Rust standard library data structures

BugHunt, Rust This project is aiming to provide "stateful" QuickCheck models for Rust's standard library. That is, we build up a random list of operat

Brian L. Troutwine 161 Dec 15, 2022
Rust testing library

K9 - Rust Testing Library Snapshot testing + better assertions Available test macros snapshot assert_equal assert_greater_than assert_greater_than_or_

Aaron Abramov 269 Dec 10, 2022
beaver is a library for setting up Rust objects inspired by factory_bot.

beaver is a library for setting up Rust objects inspired by factory_bot. Usage | Examples | Docs Dependencies [dependenci

Takayuki Maeda 40 Sep 6, 2022
Rustress - stress testing library in Rust. For fun

rustress Simple network stress testing library. To get familiar with Rust Planned features (Subject to change) Multithreaded client/server Throughput

Hakan Sönmez 7 Sep 22, 2022
insta: a snapshot testing library for Rust

insta: a snapshot testing library for Rust Introduction Snapshots tests (also sometimes called approval tests) are tests that assert values against a

Armin Ronacher 1.4k Jan 1, 2023
Simple assertion library for unit testing in python with a fluent API

Simple assertions library for unit testing in Python with a nice fluent API. Supports both Python 2 and 3.

snakedye 19 Sep 10, 2022
A tiny, super simple and portable benchmarking library.

benchmark-simple A tiny benchmarking library for Rust. Trivial to use Works pretty much everywhere, including WebAssembly (WASI, but also in-browser)

Frank Denis 3 Dec 26, 2022
This is a tiny (but delightful!) utility library for exhaustive testing.

Exhaustigen This is a tiny (but delightful!) utility library for exhaustive testing. It is based (directly) on the idea and code in the following blog

Graydon Hoare 34 Dec 14, 2022
A minimalist property-based testing library based on the arbitrary crate.

A minimalist property-based testing library based on the arbitrary crate.

Aleksey Kladov 61 Dec 21, 2022
A structure-aware HTTP fuzzing library

?? FeroxFuzz ?? A structure-aware HTTP fuzzing library ?? Another ferox? why? ?? Chill, it's not another command-line tool, this one's a library! ?? M

epi 141 Dec 27, 2022
An unofficial client library for the fuzz-introspector API.

fuzz-introspector-client An unofficial client library for the fuzz-introspector API. Quickstart Add package as a dependency; cargo add fuzz-introspect

Nathaniel Brough 4 Nov 25, 2023
Handle some lichess.org/tournament load with Rust, while learning Rust

lila-http Take some of the HTTP load away from lila. WIP! Arena tournaments Clients connected to a tournament page request new data about the tourname

Lichess 22 Jan 2, 2023
Testing Framework for Rust

Polish Polish is Test-Driven Development done right Getting Started Installing the Package The crates.io package is kept up-to-date with all the major

Fadi Hanna Al-Kass 49 Dec 18, 2022
🐇 Fuzzing Rust code with American Fuzzy Lop

afl.rs Fuzzing Rust code with AFLplusplus What is it? Fuzz testing is a software testing technique used to find security and stability issues by provi

Rust Fuzzing Authority 1.3k Jan 5, 2023
Travis CI and AppVeyor template to test your Rust crate on 5 architectures and publish binary releases of it for Linux, macOS and Windows

trust Travis CI and AppVeyor template to test your Rust crate on 5 architectures and publish binary releases of it for Linux, macOS and Windows Featur

Jorge Aparicio 1.2k Dec 30, 2022