lostisland/faraday

Tests with the combination of `strict_mode` and `FlatParamsEncoder` may fail

yykamei opened this issue ยท 6 comments

With strict_mode, the Testing adapter tries to check the equality of parameters here.

return Set.new(params) == Set.new(request_params)

This check sometimes return false with FlatParamsEncoder because an array value may contain elements by different order. For example,

stubs = Faraday::Adapter::Test::Stubs.new do |stub|
  stub.get("/foo?key=123&key=234&key=abc") do |_env|
    [200, { content_type: "application/json" }, JSON.dump(status: "OK")]
  end
end
conn = Faraday.new { |b| b.adapter(:test, stubs) }
conn.get("/foo", { key: [123, 234, "abc"] })
stubs.verify_stubbed_calls # => Error with this comparison: `Set.new(key: [123, 234, "abc]) == Set.new(key: ["abc", 123, 234])`

In general, the order of query is not important, so I want to check keys without order.

What do you think?

Related PRs: #1298 #1316

This makes sense, but I can see someone coming in future and asking how to write a test that also checks the specific order of things...
Ideally, it would be great to support both scenarios and let the developer choose which one they want to apply in any given case. I'd also feel reluctant in changing the current behaviour as this would cause existing tests relying on the adapter to fail.

A possible solution to all these points could be the following:

  • We keep the current behaviour as-is
  • We extend the test adapter capabilities so that query parameters in the stub can be passed as a separate argument, rather than as part of the URI (e.g. stub.get('/foo', query: { key: [123, 234, "abc"] }))
  • We allow to pass RSpec matchers as values for query, similarly to how Webmock allows you to do

I like this because it would be flexible enough to support all cases, while also keeping the existing behaviour, but I'm not sure about the complexity of such a change, so I'm open to alternative suggestions as far as they respect the points I highlighted above!

Oh, I understood FlatParamsEncoder has a feature to sort parameters.

params.sort! if @sort_params

As you said, it would be necessary for someone who wants to check the specific order of parameter values.

Introducing query looks great ๐Ÿ‘ Of course, the Test adapter must consider parameters included in path as well as query, but it would be useful to construct parameters with the Hash value. So, I think it's worth implementing ๐Ÿ˜„

By the way, is query good for the argument name? Faraday's methods seem to use params for the URL query parameters. For example, Connection#get has params for its method argument.

# @param params [Hash] Hash of URI query unencoded key/value pairs.

The idea behind using query and body instead of just params is that you'll be able to distinguish them on HTTP methods that support both (e.g. POST).

Of course, the Test adapter must consider parameters included in path as well as query

Good point, I think I see where this is coming from.
Could you show an example of this just to be sure we're on the same page?

OK, using query makes sense ๐Ÿ‘

I think changing here might be the first step:

path, query = normalized_path.respond_to?(:split) ? normalized_path.split('?') : normalized_path

Stub seems to have query as a string, but we will have to merge this query with the new parameter query, which comes from the argument of #new_stub. So, I'm considering changing Stub#query from string to Hash.

The example code in #new_stub here.

path, query = normalized_path.respond_to?(:split) ? normalized_path.split('?') : normalized_path
query = convert_to_hash(query) # NOTE: convert_to_hash is something like URI.decode_www_form
query.merge!(query_from_args)

stub = Stub.new(host, path, query, headers, body, @strict_mode, block)

That makes sense to me, Stub can use any internal representation for query as far as it works in the same way ๐Ÿ˜„!
Sorry I was sure I got back to you already on this ๐Ÿ™ˆ

Oh, I misunderstood the implementation of FlatParamesEncoder. I reconsidered this issue and keeping as-is might be the best way. Thanks for thinking about this issue.

By the way, adding query for testing adapters might be a nice to have. This is out of this issue ๐Ÿ˜„