dpep / webmock-twirp

MIT License
12 stars 0 forks source link

Feature: allow `with` to accept rspec matchers #80

Open jasonkarns opened 1 month ago

jasonkarns commented 1 month ago

The stub with presently expects arguments that are Protobuf::MessageExts or a hash (where the hash syntax supports constants, regexes, and rspec matchers).

This allows for fine-grained stubbing. However, when testing error flows, one likely wants more course-grained stubbing. (A frequent problem with stubbing in error flows is that the stub is overly specific. At some point the stub fails to match when it should - likely because the schema changed. The stub fails but the test is expecting an error. If the error assertions aren't written well, and they often aren't, then the test continues to pass but for the wrong reason.)

So for course grained stubbing, a more desirable matcher might be:

stub_twirp_request(:api)
  .with(be_an Schema::DomainObject)
  .to_return(Twirp::Error.unavailable("Service is unavailable"))

This type of assertion allows the stub to be much more flexible with what it matches, (all it cares about is the type of protobuf message it receives, not the contents or strict structure).

The current alternative is to either drop the .with altogether, which isn't helpful for tests that make multiple twirp calls. Another alternative is to use the block form for constructing the with matcher, but that's exceedingly verbose for a simple type comparison.

dpep commented 1 month ago

Hey @jasonkarns, thanks for reaching out! I agree with your testing philosophy of not wanting overly fine grain stubbing that leads to fragility. I'm generally a fan of rspec matchers, although they might be redundant here since specifying an rpc method will implicitly specify the request type. eg.

stub_twirp_request(:api, :rpc_method_that_uses_domain_object_request)
  #.with(be_an Schema::DomainObject)  # redundant?

and the existing attribute matching does rspec matching under the hood, eg.

  .with(have_attributes(msg: "woof"))

  # equivalent to 

  .with(msg: "woof")

side note: in terms of returning errors, these shortcuts might prove useful, eg.

stub_twirp_request(:api)
  .to_return(:unavailable)  # equivalent to your example

that said, Ruby is all about delighting developers (❤️ ) and supporting all the ways of doing things, so I'm open to this. what do you think of: https://github.com/dpep/webmock-twirp/pull/81 ?