connectrpc / connect-go

The Go implementation of Connect: Protobuf RPC that works.
https://connectrpc.com
Apache License 2.0
3.01k stars 105 forks source link

Mocking stream handlers in tests #458

Closed torkelrogstad closed 1 year ago

torkelrogstad commented 1 year ago

Is your feature request related to a problem? Please describe. I'm having trouble using a mock of a streaming RPC in tests. I'm using gomock for generating my mocks. It looks something like this:

mock.EXPECT().
    StreamingMethod(gomock.Any(), gomock.Any()). 
    Return(&connect.ServerStreamForClient[RequestType]{}, nil)

This works fine until the client actually reads from the stream. In my case the call to Receive() on the ServerStreamForClient panics, because the conn field is nil. It appears like I have no way of setting this field, or otherwise influence what's returned.

Describe the solution you'd like I think it would be reasonable to expose a way of setting a custom StreaminClientConn on the ServerStreamForClient struct. In general I'd also love more guidance on how to test my Connect server

pkwarren commented 1 year ago

Instead of using mocks, we typically spin up a httptest server and run E2E tests against it with a real client or provide a different service implementation if you want to test error handling or interceptors.

Server Example:

https://github.com/bufbuild/connect-go/blob/06547291f2a94158b2bf8cf0feb549a565f9582e/client_ext_test.go#L75-L80

Client Example:

https://github.com/bufbuild/connect-go/blob/06547291f2a94158b2bf8cf0feb549a565f9582e/client_ext_test.go#L84-L89

The tests run against a custom implementation of the service here: https://github.com/bufbuild/connect-go/blob/06547291f2a94158b2bf8cf0feb549a565f9582e/connect_ext_test.go#L2150

If you don’t want to actually run through the TCP stack, it’s pretty easy to make httptest use an in-memory net.Conn. Here’s how we do that in connect-go: https://github.com/bufbuild/connect-go/blob/81974b9f8e1ec645684f07730b113953bb081ecc/example_init_test.go#L44

Hope this helps!

akshayjshah commented 1 year ago

@pkwarren has outlined the best approach - streams are fairly complex, and mocking them (especially with gomock) is typically much more work than running through an actual HTTP stack.

torkelrogstad commented 1 year ago

Thank you for the guidance on how to do this!

anu-tiernan commented 2 months ago

@pkwarren has outlined the best approach - streams are fairly complex, and mocking them (especially with gomock) is typically much more work than running through an actual HTTP stack.

The advantage is being able to more directly inject errors to test error handling. If stream client stubs returned an interface instead of *connect.ServerStreamForClient which is what the Go gRPC library does mocking would be easy.

Some types of tests you're definitely better off using httptest or similar though.

Was hesitant to open a new issue just to add my 2c, but if I've managed to convince you to consider this again @akshayjshah I'm happy to open a fresh issue about it and state my case.