英文:
Ruby Rspec class_double that returns new REST response each call
问题
I understand your request. Here's the translated code without the comments and non-translatable parts:
class API
include HTTPI
def find_device(id)
req = create_request('path')
req.body = { :customerId => MultiJson.dump(customer_id) }
return call(req)
end
def find_other_device(other_id)
req = create_request('path')
req.body = { :other_id => MultiJson.dump(other_id) }
data = call(req)
return data
end
def call(req)
response = HTTPI.send(req)
return response.body
end
end
class Device
@api = API.new(:open_timeout => 30, :read_timeout => 30)
def get_device
devices = @api.find_device(@id)
log.info("First Call Made")
other_call = @api.find_other_device(@other_id)
end
end
RSpec.describe Device do
resp = { code: 200, body: resp_body, raw_body: "TEST BODY", cookies: [cookie_list_hash] }
resp2 = { code: 200, body: resp_body_2, raw_body: "TEST BODY 2", cookies: [cookie_list_hash] }
let!(:request) { class_double('Request', new: http).as_stubbed_const }
let!(:http) { class_double('HTTPI', send: resp).as_stubbed_const }
it 'test' do
Device.get_device
end
end
Please note that I've removed the HTML entities (e.g., "
) and corrected some symbols for clarity. If you have any specific questions or further translations, feel free to ask.
英文:
I have a Rspec test which needs to double the Request
and HTTPI
class/module and return a mocked REST response. I have this working, until this method makes another REST call and needs to return a new REST response.
API Class
NOTE this is a trimmed down version of the class but the gist is there
class API
include HTTPI
def find_device(id)
req = create_request('path')
req.body = { :customerId => MultiJson.dump(customer_id) }
return call(req)
end
def find_other_device(other_id)
req = create_request('path')
req.body = { :other_id => MultiJson.dump(other_id) }
data = call(req)
return data
end
def call(req)
response = HTTPI.send(req)
return response.body
end
end
Device
file calling REST method
class Device
@api = API.new(:open_timeout => 30, :read_timeout => 30)
def get_device
devices = @api.find_device(@id)
log.info("First Call Made")
other_call = @api.find_other_device(@other_id)
end
end
spec file
Rspec.describe Device do
resp = {code: 200, body: resp_body, raw_body: "TEST BODY", cookies: [cookie_list_hash]}
resp2 = {code: 200, body: resp_body_2, raw_body: "TEST BODY 2", cookies: [cookie_list_hash]}
let!(:request) {class_double('Request', new: http).as_stubbed_const} # I understand this causes the HTTPI send request to always return the same resp, but without it the test does not even get past the first call
let!(:http) {class_double('HTTPI', send: resp).as_stubbed_const}
it 'test' do
Device.get_device
end
end
The hope is to make a double that returns the resp var first and on the second call the :send, it returns resp2.
I am rather new to ruby also, so this may be pretty ugly.
答案1
得分: 2
I will focus on your spec, though there are some other things in your other classes that may need review (depending on what you want to achieve).
也许如果你以另一种方式编写它,你可以理解它的逻辑。
First of all, you need to define the responses as let
s as well; also, you can take a look at returning different values across multiple calls.
首先,你也需要将响应定义为 let
,并且你可以查看在多次调用中返回不同的值。
Having said that, which may resolve the problem with your spec, it seems that you also may want your api
object to be an instance variable, and not something defined in your class:
话虽如此,这可能会解决你的规范问题,但看起来你也可能希望你的 api
对象成为一个实例变量,而不是在你的类中定义的东西:
class Device
def api
# This will create the API object only once, and return it each time you call it in #get_device
@api ||= API.new(:open_timeout => 30, :read_timeout => 30)
end
def get_device
devices = api.find_device(@id)
log.info("First Call Made")
other_call = api.find_other_device(@other_id)
end
end
But, again, this depends on what you want to achieve and if the code you pasted is complete/correct or not, so sorry if this doesn't apply.
但是,再次强调,这取决于你想要实现什么,以及你粘贴的代码是否完整/正确,所以如果这不适用的话,我很抱歉。
英文:
I will focus on your spec, though there are some other things in your other classes that may need review (depending on what you want to achieve).
Maybe if you write it another way you can get the logic behind it.
First of all, you need to define the responses as let
s as well; also, you can take a look at returning different values across multiple calls.
Rspec.describe Device do
let(:resp) do
{
code: 200, body: resp_body, raw_body: "TEST BODY", cookies: [cookie_list_hash]
}
end
let(:resp2) do
{
code: 200, body: resp_body_2, raw_body: "TEST BODY 2", cookies: [cookie_list_hash]
}
end
let!(:request) { class_double('Request', new: http).as_stubbed_const }
let!(:http) { class_double('HTTPI').as_stubbed_const }
before do
# see https://www.rubydoc.info/github/rspec/rspec-mocks/RSpec%2FMocks%2FMessageExpectation:and_return
allow(http).to receive(:send).and_return(resp, resp2)
end
it 'test' do
Device.get_device
end
end
Having said that, which may resolve the problem with your spec, it seems that you also may want your api
object to be an instance variable, and not something defined in your class:
class Device
def api
# This will create the API object only once, and return it each time you call it in #get_device
@api ||= API.new(:open_timeout => 30, :read_timeout => 30)
end
def get_device
devices = api.find_device(@id)
log.info("First Call Made")
other_call = api.find_other_device(@other_id)
end
end
But, again, this depends on what you want to achieve and if the code you pasted is complete/correct or not, so sorry if this doesn't apply.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论