Of Architecture, Traits and Unit Testing

Having every test hit a database, even a local one, would greatly increase the amount of time it takes to run your unit tests. Unit tests are meant to be quick, hitting a database is not.

I agree that there needs to be some tests that go through the entire flow and hit a local database, but those should be fewer and more high level than unit tests.

There is a fair disagreement between mocking (london style testing) and not mocking (detroit style testing), and a fair argument can be made either way but discounting mocking as stupid is not the way to go.

I personally lean towards not mocking where possible and adding mocks sparingly in places that I really do not want to test again and again (making network requests, hitting a DB, rendering graphics, etc).

But this is off-topic now so I will stop here.

3 Likes

[Moderator note: Indeed, please discontinue the argument over mocking versus alternatives.]

5 Likes

You don't need, and actually can't have generalization in here. It is not actually a Rust limitation, it is logically impossible. That happens here in rocket, is creating some guard in place, so before calling your get_user_by_id, rocket calls something like let user_service = <State<...> as rocket::request::FromRequest>::from_request() which essentially calls static function from_request of your State type (which should be implemented via FromRequest trait). What from_request does, is having only rocket::Request as an argument, provides created State in place. But to create State, you need to create all its fields. Even in Java to create actual object, you need concrete type of it.

Reason why you cannot create State<dyn Trait> is because it has no known size - it may be any size depending on what actual object is passed to it. It can be easly worked around - what you need is to implement UserService for Box<dyn UserService> like this:

impl UserService for Box<dyn UserService> {
  fn foo(&self) {
    self.deref().foo()
  }
}

and then create State<Box<dyn UserService>>. The problem is, that after all implementation of FromRequest enforces you to create Box of actuall type which is just made to be a trait object, so you gains nothing, but losses performance (because of dynamic dispatch). I don't say, that it is a significant performance lose, but you make much work to only loose - doesn't seem like profit for me.

If you want to test your API functions mocking your state, you may go with what @alice proposed, but actually I think, that all those functions should be just single calls to State, so testing them seems like overkill.

And also for increased readability - you should go with @17cupsofcoffee hint, and add default type argument for your State for the one, which you are implementing FromRequest for - this would make your main path in code clearer, and you would specify generic argument only for tests purposes.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.