-
Notifications
You must be signed in to change notification settings - Fork 555
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
FEATURE: Allow asserting on requests in tests #1069
base: master
Are you sure you want to change the base?
FEATURE: Allow asserting on requests in tests #1069
Conversation
# Points to an invalid or recycled object so ignore | ||
end | ||
@request_object_ids[key] = key.object_id | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bblimke this is missing any comments to explain what's going on - maybe worth me adding some?
For others - doing this so that we are just referencing existing requests in the array, rather than creating new objects.
This is so that if we have massive numbers of requests that have the same signature, we're not bloating memory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@johngallagher Thanks for your work on this. I've been looking at the store_to_array
method.
I think it might be helpful to add a comment explaining the intention behind using object_id
and _id2ref
. This would really help to understand the reasoning behind this approach.
That said, I believe we might be able to simplify this code. I'm not sure whether we really need object_id
and _id2ref
at all. From my understanding, array
is just storing references to the original objects, not clones.
I think the following achieves the same result:
def store_to_array(key, num)
@request_objects ||= {}
stored_object = @request_objects[key] ||= key
num.times do
array << stored_object
end
end
This version uses a hash (@request_objects
) to store the original request signature objects with same #hash
result. It maintains the original object without the need for object_id
.
It should be more efficient as it avoids the overhead of ObjectSpace._id2ref
and potential RangeError
rescues,
though I don't know how much faster that is.
Can you think of any scenarios where the original implementation behaves differently from this proposed one?
What are your thoughts on this?
I wonder if this can me optimised even further.
Gah @bblimke all the tests are failing and I realised I need to do some documentation (I'll do that in a separate PR if that's OK?) I'll come back to this and debug. |
I'm getting a ton of unrelated CI failures locally - no idea what's going on here. These tests are all failing on Happy to pair with you some time @bblimke to make this all green again. I'm going to leave this for now as I'm not best placed to do a deep dive into the codebase... |
@@ -44,6 +44,10 @@ def json_headers? | |||
!!(headers&.fetch('Content-Type', nil)&.start_with?('application/json')) | |||
end | |||
|
|||
def parsed_body |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@johngallagher Thank you for this feature. I understand the intention behind this method. I have some concerns though. The current implementation assumes JSON, but request bodies can come in various formats (e.g., form-url-encoded, XML). This method would raise errors for non-JSON payloads. Additionally, while symbolized keys can be useful, it's often a matter of user preference rather than a universal expectation.
To address these issues, we could either expand parsed_body
to support multiple formats, or allow users to apply their own decorators for custom parsing logic. Given these considerations, I suggest treating this as a separate feature. This would allow us to design a more flexible solution that caters to various use cases and preferences.
What are your thoughts on this approach?
@@ -5,22 +5,36 @@ | |||
module WebMock | |||
module Util | |||
class HashCounter | |||
attr_accessor :hash | |||
attr_accessor :hash, :array |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@johngallagher I believe we should separate the array of requests from the HashCounter. The HashCounter has a specific, single responsibility, and adding an array of requests expands its scope beyond its intended purpose.
Instead, I suggest moving the ordered list of requests up to the RequestRegistry object. This way, the RequestRegistry would manage both the HashCounter and the array of requests, maintaining a clearer separation of concerns.
What are your thoughts on this restructuring? It would allow the HashCounter to remain focused on its core functionality.
request_object_id = key.object_id if request_object_id.nil? | ||
num.times do | ||
array << ObjectSpace._id2ref(request_object_id) | ||
rescue RangeError |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@johngallagher have you experienced a scenario where that rescue
is needed? when can that happen?
@johngallagher all tests in master branch are passing now, therefore feel free to marge master branch to this one. |
Context
@jamesshore has created the concept of "Testing With Nullables".
A small snippet:
I've been experimenting with these techniques in various codebases and love the code that results.
Why
This style of assertions, whilst not adhering to James' full pattern, is a thin layer on top of what Webmock is already doing - @jamesshore calls it "Output Tracking"
Example
Let's say we're doing an API request to Cloudflare to get IP blocking rules.
Before
Strengths
Weaknesses
#have_requested
RSpec matcher means more codeAfter
Strengths
#requests_made
and we'd be goodWeaknesses
#requests_made
needs a mixin to workAfter - Cleaner
How
HashCounter
class to store requests in an array (see comment for details)parsed_json_body
method ontoWebmock::RequestSignature
#requests_made
onto the registry