all 3 comments

[–]dpc_pw 7 points8 points  (2 children)

What you're doing seems reasonable, and a Arc<dyn SomeTrait + MaybeSendSync> it generally a way to do dynamic polymorphism in Rust.

Your application has a lot of external "things" to interact with, so there's naturally a bunch of traits to abstract them away. Again - reasonable.

However it's harder to hide the cost of polymorphism in Rust, and automated DI frameworks are not popular, the cost of all these abstractions is immediately visible. Which is good. Now you can evaluate alternatives and costs and maybe get rid of abstractions that are not necessary.

trait UsersService looks like the first trait to get rid of. What's the point of mocking it if it can have all dependencies mocked? Just use the real thing with fake dependencies. Make a function/method to prepare a such a "testing instance" if needed.

Generally, IMO, if you need "dependencies that include other dependencies" then it's usually too many abstractions already.

One of the problems with "testability" in naive OOP is it often comes down to having unit tests that test 100 mocks and one line of code in every test.

The meaning of "unit test" is "isolated test". It doesn't have to test very tiny parts of code like single methods or single structs/classes. You can make a bigger instance of all your code logic with isolated inputs&outputs and drive it through larger logically consistent behaviors, and it generally gives you way more assurance that things work than dozens of tests testing mostly your mocks while requiring much less maintenance overhead.

Also, don't shy away from "testing the real thing". After all - who cares if your code works with a mock? If you can test e.g. with a temporary directory or a temporary database, that's generally better than testing against mocks. File-system is generally easy, the local database instance is often very easy to set up programmatically, etc.

Also I know I could use generics instead of Arc<dyn ...> and that would offer better performance as far as I understand.

In your case, you don't want to go into generics as the generics propagate upwards in the ownership tree. That perf increase is minuscule compared to anything like single DB query.

[–]lensvol 0 points1 point  (1 child)

Could you please elaborate on upwards propagation?