Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generated Clients should have a trait defining the methods it implements for proper mocking #809

Closed
Jake-S6 opened this issue Oct 22, 2021 · 2 comments

Comments

@Jake-S6
Copy link

Jake-S6 commented Oct 22, 2021

Feature Request

Generated Clients should have a trait defining the methods it implements for proper mocking. The important case being any code that utilizes these clients cannot mock it out properly to make focused unit tests without additional overhead dev work. We have to add boilerplate code defining a trait with the same method signatures and implementing that new trait with the generated tonic client ourselves per client instead of having it done as part of the code generation step. This approach is more brittle and has higher dev effort for anyone using Tonic Clients that want to test their code properly.

We already have this for the generated Server code, so adding it to Client seems to fall in line with the design.

Crates

  • tonic-build

Motivation

Being able to follow the standard unit test approach using proper mocking by default.

Proposal

For a service

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

instead of having

    impl<T> GreeterClient<T>
    where ...
    {
...
        pub async fn say_hello(
            &mut self,
            request: tonic::Request<super::HelloRequest>,
        ) -> Result<tonic::Response<super::HelloReply>, tonic::Status> {
           ...
        }
    }

we should have something along the lines of

   pub trait Greeter: ... {
        async fn say_hello(
            &self,
            request: tonic::Request<super::HelloRequest>,
        ) -> Result<tonic::Response<super::HelloReply>, tonic::Status>;
    }

    impl Greeter for GreeterClient<T>
    where ...
    {
...
        pub async fn say_hello(
            &mut self,
            request: tonic::Request<super::HelloRequest>,
        ) -> Result<tonic::Response<super::HelloReply>, tonic::Status> {
           ...
        }
    }

Alternatives

The only alternative I see is to stick to the current state and require the additional overhead on the devs using tonic Clients.

@LucioFranco
Copy link
Member

Mocking can be achieved by using something like tokio::io::duplex stream. Here is an example https://github.com/hyperium/tonic/blob/master/examples/src/mock/mock.rs adding a trait for clients imo is not the correct solution here since traits add complexity to an already decently complex system where you don't really need it. Going to close this issue, feel free to reopen if you have more questions. :)

@Sushisource
Copy link
Contributor

@LucioFranco I'll just throw out there one other reason besides just mocking is to enable wrapper types. Right now for example this is how I implement some retry logic reasonably generically as a workaround for the issues mentioned here: #733

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants