我想测试一个执行REST请求的API客户端。请求如下:
# vcr/attachments.yml
- method: POST
- path: http://example.org/attachments
- body: { "filename": "foo.jpg", "signature": "6g33jk2C1QQn9EM8Q==" }
- response: 200 OK
- method: POST
- path: http://example.org/attachments
- body: { "filename": "bar.jpg", "signature": "7z44g6aPPk2C17Xf5==" }
- response: 409 Conflict
我正试着用录像机模拟这些请求。在相关测试中,我写道:
VCR.use_cassette('attachments', match_requests_on: [:host, :path, :body_as_json]) do
my_record.attach_all(['foo.jpg', 'bar.jpg'])
assert_nil my_record.errors['foo.jpg'] # should succeed with 200
assert_present my_record.errors['bar.jpg'] # should fail with 409
end
唯一区别这两个请求的是"filename"="foo.jpg"
主体参数,所以我需要在请求主体上进行匹配。
但问题是signature
参数本质上是随机的,或者至少不能一致地预测(例如,它在CI服务器上发生变化(。因此,整个身体的匹配是不稳定和不可靠的。
我怎样才能确保VCR符合正确的录制要求,即使机身永远不会完美匹配?
当然,最简单的解决方案就是将磁带盒一分为二。然后你可以一个接一个地匹配它们:
VCR.use_cassette('attachment_foo', match_requests_on: [:host, :path]) do
my_record.attach_all(['foo.jpg'])
assert_nil my_record.errors['foo.jpg'] # should succeed with 200
end
VCR.use_cassette('attachment_bar', match_requests_on: [:host, :path]) do
my_record.attach_all(['bar.jpg'])
assert_present my_record.errors['bar.jpg'] # should fail with 409
end
但是,如果由于某种原因无法分割请求(可能是因为需要将它们分批在一起(,那就行不通了。
在这种情况下,更详细的解决方案是使用自定义VCR匹配器。
# Match the JSON body of the request, ignoring some specified keys.
def body_as_json_excluding(*keys)
lambda do |r1, r2|
begin
keys = keys.map(&:to_s)
JSON.parse(r1.body).except(*keys) == JSON.parse(r2.body).except(*keys)
rescue JSON::ParserError
false
end
end
end
(请参阅此处的最新要点:https://gist.github.com/kemenaran/2dcc463fdda3e476983bf5500f3524b9)
在测试中,您可以告诉VCR忽略JSON主体的signature
部分:
VCR.use_cassette('attachments', match_requests_on:[
:host,
:path,
body_as_json_excluding(:signature)
]) do
my_record.attach_all(['foo.jpg', 'bar.jpg'])
assert_nil my_record.errors['foo.jpg'] # should succeed with 200
assert_present my_record.errors['bar.jpg'] # should fail with 409
end
VCR现在将与整个JSON匹配,除了可能发生不可预测变化的部分。