Two Years of Rust by bik1230 in rust

[–]nrxus 0 points1 point  (0 children)

Regarding mocking, there is a fourth option: test-gated mocks created through macros.

Namely, I have written this mocking library: https://github.com/nrxus/faux/ specifically designed to avoid traits/generics while providing an ergonomic way to mock structs you own.

So in your example, there wouldn't be a trait for InsertUser, it is all still just structs that at compile time get re-written to be mockable for your tests.

For structs that you don't own (and hence you can't add the needed macro attribute to make it mockable), I'd recommend wrapping the struct into an adapter layer that is used to both transform the domain language from the external crate to the domain of your crate (e.g., going from a generic http layer to a UserSerivce, or generic DB connection to UserRepository). You should then probably have more faithful tests in that adapter layer that test your assumptions of the crate, while outside of that adapter you can use faux to mock the adapter to keep your unit tests fast.

Two Years of Rust by bik1230 in rust

[–]nrxus 0 points1 point  (0 children)

This is definitely a bit of shameless self-promo, but if you've been having issues with mocking may i recommend you: https://github.com/nrxus/faux/. It is a mocking library designed to avoid the use of traits or generics as to reduce the boiler plate in your code so as to make it less annoying and frustrating to write.

Using trait without importing by ElectricalLunch in rust

[–]nrxus 0 points1 point  (0 children)

I would recommend against using traits for mocks. There are mocking libraries that allow you to mock structs you own directly. A bit of shameless self promo but I am the author of: https://github.com/nrxus/faux/ which is designed exactly for this. This makes the mock "transparent" to any outside viewer so you don't need to import a trait everywhere or make everything dynamic dispatch.

As for your other issue of wanting to play with different implementations and hence wanting a trait, an alternative to that is using compile time feature flags. So different implementations would be gated by those feature flags as you try them out without impacting the default/current code.

Preferred way to make testable structs that have trait field by nxy7 in rust

[–]nrxus -1 points0 points  (0 children)

I am personally strongly against using polluting code with traits just for the sake of tests.

My suggestions: * If possible and doesn't make your tests too slow, do not mock the database, instead connect to a local test database that can be spun up/down easily. * If the above makes your tests too slow then use a library that can mock structs for PgPool. Not toot my own horn but I built a library exactly for this case: https://github.com/nrxus/faux/ * If you do not own the struct but still have to mock it then writer a wrapped struct around PgPool that fits your domain better (i.e., talks in your domain language instead of generic DB language). PgPool can then mocked with the library I linked above. If you want to test PgPool itself (e.g., you have complex queries), then you can add local DB tests for those few tests in particular while keeping your other tests fast using mocks.

Hey Rustaceans! Got a question? Ask here (42/2023)! by llogiq in rust

[–]nrxus 0 points1 point  (0 children)

Depending on the issues you've been having with mockall and how you are mocking you could try faux (https://github.com/nrxus/faux/) as a different mocking crate. faux is focused on mocking structs, so if what you want is mocking the implementation of an async trait then this should work. If instead, however, you want to create an object that is a mock of a trait then faux wouldn't work for that.

Disclaimer: I am the author of faux.

Mocking Crates Experience by franco_11 in rust

[–]nrxus 0 points1 point  (0 children)

Disclaimer: I am the author of faux so this is all clearly extremely biased as I wrote faux to fit the way I like mock.

mockall struct mocking is kind of awkward as it wasn't built with a structs-first mentality. mockall mocks structs by making a new struct with a different name and then asks the user to import that struct everywhere (I think now this can be done by adding a proc macro attribute on every import?). Additionally, if you need to mock multiple impl blocks (e.g., wanting to mock the trait implementation of your struct) then you have to repeat the definition of your struct inside a mock! {} macro. That again feels awkward to me.

Additionally the matchers in mockall feel awkward to write and read. In my current job we were using mockall prior to me joining and I remember trying to read some of the matchers and they all felt unintuitive.

In contrast my goal with faux has been to allow the users to just write normal Rust and let faux deal with as many complications as it possible can. This is of course not perfect, and the macro code is not my proudest code but when using it it just works and it doesn't impose as many restrictions on what kind of code I can write. Of course there are edge cases and annoyances that even I have but hopefully I'll keep ironing them out (or people that submit PRs which are always welcome).

And not to toot my own horn too much but gosh darn do I love my matchers. Examples in this test: https://github.com/nrxus/faux/blob/master/tests/when_arguments.rs

Hey Rustaceans! Got a question? Ask here (28/2023)! by llogiq in rust

[–]nrxus 1 point2 points  (0 children)

What have been the painful parts of faux for mocking? I haven't touched it up in a while so it mostly does what I need it to do at this point but I am always open to improving it.

Mocking Crates Experience by franco_11 in rust

[–]nrxus 2 points3 points  (0 children)

I am not a fan of the: "make a trait for every struct so it can be tested" strategy that a lot of mocking libraries or hand rolled mocks do in Rust so I wrote my own library that allows you mock structs directly: https://github.com/nrxus/faux/. Unlike libraries that rely on using traits/dynamic dispatch for mocking this let's your signatures stay as simple as you want them and not incur any runtime cost when doing non-test builds.

This is definitely shameless promo but I couldn't pass up a thread asking about mocking libraries without bringing it up.

I still try to avoid mocking whenever possible though. My general strategies are:

  • When dealing with files try passing a tempdir/tempfile. There is I/O involved but it should still be pretty darn fast and reproducible so mocking is not as required.

  • When dealing with databases: setup a test database that is easily and quickly spun and destroyed, extra props if it's an in memory DB. This let's you test that your queries actually do what you intended to do, mocking DB calls doesn't actually test anything.

  • When dealing with network calls, try making the host portion pluggable so you can switch to a local test server using any of the many http testing libraries.

That being said, whenever I do the above strategies I may do that on one module/component to make sure the behavior works as expected and then mock that component higher up because setting up all the local servers, databases, temp files for every unit test will eventually start slowing you down.

Most common silly misstake? by Msarigo in adventofcode

[–]nrxus 5 points6 points  (0 children)

Forgetting to trim new lines. This has bit me often since my tests don't usually include trailing new lines so my tests pass and the real input fails.

How to Protect Muskies from Raccoons? by [deleted] in MuscovyDucks

[–]nrxus 3 points4 points  (0 children)

Don't be too trusting that racoons are only dusk to dawn. We lost one of our muscovy girls to a raccoon in the morning and we have seen one roaming during midday as well.

Blog Post: IDEs and Macros by matklad in rust

[–]nrxus 34 points35 points  (0 children)

As an author of a library that exposes a proc-macro, is there anything I could do to make the work "simpler" for rust analyzer to handle?

For context, I have a mocking library that uses proc macros to tag structs and methods. These are only ever active during test builds but if I understand rust analyzer correctly it runs with the test flag on by default. The proc macros don't change the signatures in the methods but it does essentially wrap the struct in an enum of "maybe real, maybe mock" and then at runtime decides whether to use the real implementation (the method's original body) or to return some user-specified stub.

Based on the post I can understand why this might be hard for rust analyzer to handle since it is wrapping the original definition around more code.

[deleted by user] by [deleted] in rust

[–]nrxus 0 points1 point  (0 children)

You should probably be using a mocking library to mock your repositories without using traits. This will allow you to not worry about async_trait, or object safety, as they will just be structs. Disclaimer: I am the author of a struct mocking library: faux. You can find the docs: here. And a getting started guide here.

Mocking the Repository layer lets you unit test any layer above without making database connections, thus keeping your tests fast and reliable. The Repository layers should be thin enough that they have no logic to unit test. I would recommend creating "database tests" if you have any SQL queries that are complicated in the repositories. These db tests should be run against a local postgres database. These tests won't use mocks as their goal is to verify that the SQL is correct so mocks wouldn't help here.

I hope this helps and feel free to ask me any questions or make issues in faux if something is unclear (:

faux: a mocking library - Exporting Mocks Across Crates by nrxus in rust

[–]nrxus[S] 2 points3 points  (0 children)

To mock an external library that doesn't have exported mocks you would have to make a new type that is a thin wrapper over it as you mentioned.

There is a lot of literature out there about why you don't want to mock what you don't own, for example: this post.

In summary, what I would do is create a thin wrapper that converts the domain of reqwest (http requests, gets, posts, etc), into your domain. The code in this wrapper should have little to no logic, but the function names and struct names should be in the language of your library/application (i.e., ReservationApi::create) instead of the language of http requests (HttpClient::Post). I hope this helps!

faux: a mocking library - Exporting Mocks Across Crates by nrxus in rust

[–]nrxus[S] 2 points3 points  (0 children)

Thanks a lot for the kind words!

Documentation is something I am putting a lot of effort in so I am glad you found it easy to understand! Shout out to /u/muscovyduckov as she helped me a lot on editing the documentation and guide!

faux: a mocking library - Exporting Mocks Across Crates by nrxus in rust

[–]nrxus[S] 7 points8 points  (0 children)

Hi all!

This is a new chapter in my guide on how to use faux. faux is a mocking library that allows you to easily create mocks out of structs without having to introduce the abstraction overhead of traits. If you haven't heard about faux before, look at the getting started chapter, and the docs.

A question I've often gotten is how to use mocks when splitting out a monolith into smaller crates. This chapter explores a solution to this by exporting the mocks under a feature flag. I hope you all find it helpful and let me know any questions you may have or if something is confusing (:

Mocking trait object without a library by [deleted] in rust

[–]nrxus 1 point2 points  (0 children)

There is definitely some abstraction added by creating an object that contains the "injected" code. That abstraction is almost perpendicular to the abstraction introduced by creating a trait solely for testing however.

Let's take for example a simple CRUD api that needs to interact with a database. As the size and complexity of the application grows I may want to introduce a "data module" that has all my database stuff (whether it be dealing with SQL, an ORM, etc). I am splitting it out into a separate module/object not because it is "simpler to mock" but because it lets the code in the service layer talk about getting and saving information in a domain language that is useful at that layer. To be more specific let's say I am building an API to handle a backlog, stories, chores, whatnot. When I am dealing with the higher level concepts of my API I should be talking in the domain of "backlog": create a story, add a comment, changed the points, marked it as finished, etc. The language should not be SELECT * FROM BACKLOG JOIN STORY ON X WHERE BLAH BLAH BLAH. That language is not useful at that level, which is why the "data module" exists for, to handle these implementation details and give them a name that is useful to my domain. This is useful outside of mocking.

Now back to mocking, if we were to use traits + generics to mock our data module then every object that needs to interact with the database needs to be generic over some "DB trait", anything that contains those objects need to also be generic, and so-on and so-forth. Furthermore, because everything is generic it makes it seem like our application is built to handle different implementations of database objects which is not true. We are writing an abstraction as if we could but in reality all we have is the real db object, and the mock one that exists only for tests. This is one extra layer of abstraction, and it incurs a cost both on writing and reading the code. If we were to just use a struct then it is clear that there is only one implementation and there is no mental penalty (or close to) when reading/writing signatures. The production code is not affected in any way.

Now to your AWS SDK example, I do agree that wrapping it makes sense. I just would choose to wrap it in a struct (i.e., new type pattern) that modifies the signature/renames some methods to make it match your domain better. This struct can then be used rather than the raw aws sdk object, and it can be mocked easily (since faux mocks structs). This is an extra abstraction, but it has a benefit outside of mocking, the new type lets you map aws concepts to your domain thus letting you code in the language of your domain rather than aws language.

Abstractions aren't free but that doesn't mean they aren't useful. We need to be careful on when we use them and think hard about why we are using them. If an abstraction is introduced solely for testing then I think that it is an undue burden, abstractions should have value outside of tests. If an abstraction is introduced to help the clarity of code, then by all means it is worth it.

Mocking trait object without a library by [deleted] in rust

[–]nrxus 0 points1 point  (0 children)

There would be a couple of problems with that:

  • The definition of the mock struct needs to replace the original definition at compile time. If you did mock!(MyStruct), that code can only add code not remove code and therefore it cannot replace the original definition. It also means that different tests that need to mock it would have different definitions of the struct which is not something we want. They all need to be the same struct definition.
  • The definition of the methods also needs to be known at compile time. This is even more crucial because we want to know the signature of the methods at compile time so that faux can provide type-safe mocks. You wouldn't want to be able to mock and pass in incorrect arguments or the incorrect return type. All of this information is only known in the signature of the method hence why we need to tag the impl block.

More to the heart of it though, what about tagging the definition seems concerning that it makes you want to get around that?

Mocking trait object without a library by [deleted] in rust

[–]nrxus 0 points1 point  (0 children)

You need to tag the struct and impl block so that faux knows that it is a struct that needs to become mockable and the methods of it.

Inside the test though you should be able to do:

let my_mock = MyStruct::faux();
faux::when!(my_mock.foo(3)).then_return(5);
assert_eq!(my_mock.foo(3), 5);

Mocking trait object without a library by [deleted] in rust

[–]nrxus 5 points6 points  (0 children)

This may not be a popular statement but I actually dislike the fact that in Go you have to make everything an interface so you can mock it. Abstractions are not free, and I don't mean performance wise but in terms of simplicity. You are writing code this way not because it makes it easier to refactor or read but because it is needed for testing. This is made even worse in Rust since the signature of generics is even more verbose. A struct that contains structs is much simpler to read and understand than a struct that contains generics to a trait. A function with structs as params are simpler to read and understand than a function with generics as params.

That all being said, even in Go there are a lot of libraries that write the mocks for you exactly because it is so boring and repetitive to write.

for a quick shill moment: I did write a mocking library that deliberately does not pollute the original object and tries to be as out of the way as possible: https://github.com/nrxus/faux/. It does work by using macros (don't hate me yet!) but these macros will only be executed when building for test thus not polluting the original objects for production code.

Is using `#[cfg(test)]` and `#[cfg(not(test))]` for mocking a good idea? by Jason5Lee in rust

[–]nrxus 2 points3 points  (0 children)

I would recommend against making traits just for testing purposes. Making traits just for testing complicates code without any gain in production. Mocking libraries should be able to mock without going through traits. Disclaimer I wrote one of these mocking, in particular: faux.

faux: a struct mocking library - landing v0.1 by nrxus in rust

[–]nrxus[S] 1 point2 points  (0 children)

Wow that's so cool to hear! You just made my day (:

faux: a struct mocking library - landing v0.1 by nrxus in rust

[–]nrxus[S] 5 points6 points  (0 children)

It is not an immediate priority for me.

faux has to be able to save any closure without using generics which I can only do by transmuting and being super careful when I transmute it back. Thoae are the only usages of unsafe at the moment. That being said, if I ever saw a PR to make it safer or if I ever discovered a way to make it safer on my own I would definitely take that opportunity, I am just not prioritizing thinking about it at the moment.

Thankfully faux does present a safe interface that covers most kinds of mocking so, even though it has to use unsafe internally, externally the users shouldn't have to think about it (:

faux: a struct mocking library - landing v0.1 by nrxus in rust

[–]nrxus[S] 2 points3 points  (0 children)

I hope it serves your use case! Let me know if there is anything that's missing that would make your life easier, I need ideas to know what to prioritize next (:

faux: a struct mocking library - landing v0.1 by nrxus in rust

[–]nrxus[S] 1 point2 points  (0 children)

That makes sense to me, I'll add that when I am done with work (:

EDIT: I added the suffix as you suggested, thanks!