当body的某个部分是可变的并且无法预测时,我如何匹配VCR请求



我想测试一个执行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匹配,除了可能发生不可预测变化的部分。

最新更新