使用 rspec 在 ruby 中测试外部 Api
Testing External Api in ruby using rspec
我正在做一个项目,我必须测试正在调用 github 的外部 api。
我们以下面的代码为例,下面的class没有初始化器
Class CodeReviewSignedOff
def pullrequestreview(github)
#the following line return a json response, it require two parameter a string and a number
github.client.pull_request_reviews("string",number).each do |github|
#block of code which perform some operation
end
return count #return integer response
end
end
如您所见,我的 class CodeReviewSignedOff 依赖于我现有项目中定义的 github 对象,并使用该对象进行外部 API 调用。
但问题是,我不想实际调用 API.
我想要存根请求github.client.pull_request_reviews
allow(github).to receive_message_chain(:client,:pull_request_review_requests).and_return json
我的问题是,
我应该如何 return json 响应,因为如果我将它包含在双引号中,它将被解释为字符串。
我的第二个问题
如何测试我的 pullrequestreview 函数。
github=double #I am creating a fake github
object=CodeReviewSignedOff.new
expect(pullrequestreview).with(double).and_return(3)
以上逻辑是否正确?
问得好!
免责声明:我仍在学习 RoR,但我会尽力回答这个问题。如果有人对我如何改进我的回复有任何建议,请告诉我!
正在测试 class 方法(使用 stub
s)
因此,为了测试名为“pullrequestreview”的方法,您需要 stub
Github::Client
class 中的一个方法。我个人没有使用过 GitHub 的 API,所以我们假设它是一个静态的 class,带有一些 class 方法(包括 pull_request_reviews
)。
(注意:我将“pullrequestreview”重命名为pull_request_review
,因为它在Ruby中更加地道)
在下面的示例中,我存根了 Github::Client.pull_request_reviews
,这样无论何时调用它都会 return res_data
:
Github::Client.stubs(:pull_request_reviews).returns(res_data)
接下来,我使用“工厂”生成一个 github_client
参数传递给 code_review_sign_off.pull_request_review
。由于 GitHub
方法被存根,所以我传递给它的内容并不重要。在更复杂的测试场景中,使用 factory bot gem 构建不同的 GitHub
相关对象(具有不同的 trait
选项)可能是个好主意。例如,使用工厂,您可以定义一个带有“pull_request”特征的新 github_client
,如下所示:
github_client = create(:github_client, :with_pull_request)
在您的 post 中,code_review_sign_off.pull_request_review
需要来自 GitHub
的 iterable
对象。假设 code_review_sign_off.pull_request_review
return 是数组中的元素数。因为我可以控制 GitHub 的响应,所以我知道只有一个元素会被 returned,所以结果应该是 1
,我使用:
断言
assert_equal pull_request_review_response, 1
因此,通常在测试时,您希望隔离您感兴趣的函数并使用 stub
s 来保持其他一切不变。
require 'test_helper'
class CodeReviewSignedOffTest < ActionDispatch::IntegrationTest
test 'with CodeReviewSignedOff method' do
res_data = [{
:id => 1,
:data => "some_data"
}]
Github::Client.stubs(:pull_request_reviews).returns(res_data)
github_client = create(:github_client, :with_pull_request)
code_review_sign_off = CodeReviewSignedOff.new
pull_request_review_response = code_review_sign_off.pull_request_review(github_client)
...
assert_equal pull_request_review_response, 1
end
end
(注意:我在例子中使用的是mocha/minitest
)
奖励:模拟 API 端点
因此,首先,我假设您已正确配置 RSpec 的测试环境(您 运行 rails generate rspec:install
命令)并且您有适当的 /spec
目录。
模拟 API 端点的第一步是通过使用像 WebMock 这样的 gem 来存根对实际端点的调用。因此,安装 gem 并将 require 'webmock/rspec'
添加到 /spec/spec_helper.rb
在 /spec/spec_helper.rb
中,您需要定义一些内容,
首先,添加以下内容以确保您不会继续对 API 进行真正的调用:
WebMock.disable_net_connect!(allow_localhost: true)
接下来,您需要为每个要模拟的 API 端点创建一个存根。例如,在您的情况下,您需要定义如下内容:
RSpec.configure do |config|
config.before(:each) do
pull_request_review_response = [
{
"id": 80,
"node_id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3ODA=",
"user": {
"login": "octocat",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"body": "Here is the body for the review.",
"state": "APPROVED",
"html_url": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80",
"pull_request_url": "https://api.github.com/repos/octocat/Hello-World/pulls/12",
"_links": {
"html": {
"href": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80"
},
"pull_request": {
"href": "https://api.github.com/repos/octocat/Hello-World/pulls/12"
}
},
"submitted_at": "2019-11-17T17:43:43Z",
"commit_id": "ecdd80bb57125d7ba9641ffaa4d7d2c19d3f3091",
"author_association": "COLLABORATOR"
}
]
owner = 'octocat',
repo = 'hello-world',
pull_number = 42
stub_request(:get, "https://api.github.com/repos/#{owner}/#{repo}/pulls/#{pull_number}/reviews").
to_return(status: 200, body: pull_request_review_response.to_json,
headers: {'Accept'=>'*/*', 'User-Agent'=>'Ruby'})
end
# ....
end
此代码的重要部分是 stub_request
,它采用 HTTP 请求方法、URL 路径,并且 return 将参数传递给 to_return
(作为 HTTP 响应)。响应对象将包含 status
、body
和 header
.
我使用 examples from the WebMock README and the API doc from GitHub 生成了上面的示例(注意: 未经测试)。
现在,您可以定义一个调用 Mock 端点的新测试:
require 'spec_helper'
class CodeReviewSignedOffTest < ActionDispatch::IntegrationTest
test 'GET pull_request_review_response endpoint from GitHub mock' do
uri = URI('https://api.github.com/repos/octocat/hello-world/pulls/42/reviews')
response = Net::HTTP.get(uri)
data = JSON.parse(response&.body)
...
end
end
希望您能看到您在模拟 API 中定义的数据。我还没有测试过这段代码,所以它可能需要一些调试。
如果您有任何问题或 运行 在编写测试时遇到问题,请告诉我!祝你好运。
我正在做一个项目,我必须测试正在调用 github 的外部 api。
我们以下面的代码为例,下面的class没有初始化器
Class CodeReviewSignedOff
def pullrequestreview(github)
#the following line return a json response, it require two parameter a string and a number
github.client.pull_request_reviews("string",number).each do |github|
#block of code which perform some operation
end
return count #return integer response
end
end
如您所见,我的 class CodeReviewSignedOff 依赖于我现有项目中定义的 github 对象,并使用该对象进行外部 API 调用。 但问题是,我不想实际调用 API.
我想要存根请求github.client.pull_request_reviews
allow(github).to receive_message_chain(:client,:pull_request_review_requests).and_return json
我的问题是, 我应该如何 return json 响应,因为如果我将它包含在双引号中,它将被解释为字符串。
我的第二个问题 如何测试我的 pullrequestreview 函数。
github=double #I am creating a fake github
object=CodeReviewSignedOff.new
expect(pullrequestreview).with(double).and_return(3)
以上逻辑是否正确?
问得好!
免责声明:我仍在学习 RoR,但我会尽力回答这个问题。如果有人对我如何改进我的回复有任何建议,请告诉我!
正在测试 class 方法(使用 stub
s)
因此,为了测试名为“pullrequestreview”的方法,您需要 stub
Github::Client
class 中的一个方法。我个人没有使用过 GitHub 的 API,所以我们假设它是一个静态的 class,带有一些 class 方法(包括 pull_request_reviews
)。
(注意:我将“pullrequestreview”重命名为pull_request_review
,因为它在Ruby中更加地道)
在下面的示例中,我存根了 Github::Client.pull_request_reviews
,这样无论何时调用它都会 return res_data
:
Github::Client.stubs(:pull_request_reviews).returns(res_data)
接下来,我使用“工厂”生成一个 github_client
参数传递给 code_review_sign_off.pull_request_review
。由于 GitHub
方法被存根,所以我传递给它的内容并不重要。在更复杂的测试场景中,使用 factory bot gem 构建不同的 GitHub
相关对象(具有不同的 trait
选项)可能是个好主意。例如,使用工厂,您可以定义一个带有“pull_request”特征的新 github_client
,如下所示:
github_client = create(:github_client, :with_pull_request)
在您的 post 中,code_review_sign_off.pull_request_review
需要来自 GitHub
的 iterable
对象。假设 code_review_sign_off.pull_request_review
return 是数组中的元素数。因为我可以控制 GitHub 的响应,所以我知道只有一个元素会被 returned,所以结果应该是 1
,我使用:
assert_equal pull_request_review_response, 1
因此,通常在测试时,您希望隔离您感兴趣的函数并使用 stub
s 来保持其他一切不变。
require 'test_helper'
class CodeReviewSignedOffTest < ActionDispatch::IntegrationTest
test 'with CodeReviewSignedOff method' do
res_data = [{
:id => 1,
:data => "some_data"
}]
Github::Client.stubs(:pull_request_reviews).returns(res_data)
github_client = create(:github_client, :with_pull_request)
code_review_sign_off = CodeReviewSignedOff.new
pull_request_review_response = code_review_sign_off.pull_request_review(github_client)
...
assert_equal pull_request_review_response, 1
end
end
(注意:我在例子中使用的是mocha/minitest
)
奖励:模拟 API 端点
因此,首先,我假设您已正确配置 RSpec 的测试环境(您 运行 rails generate rspec:install
命令)并且您有适当的 /spec
目录。
模拟 API 端点的第一步是通过使用像 WebMock 这样的 gem 来存根对实际端点的调用。因此,安装 gem 并将 require 'webmock/rspec'
添加到 /spec/spec_helper.rb
在 /spec/spec_helper.rb
中,您需要定义一些内容,
首先,添加以下内容以确保您不会继续对 API 进行真正的调用:
WebMock.disable_net_connect!(allow_localhost: true)
接下来,您需要为每个要模拟的 API 端点创建一个存根。例如,在您的情况下,您需要定义如下内容:
RSpec.configure do |config|
config.before(:each) do
pull_request_review_response = [
{
"id": 80,
"node_id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3ODA=",
"user": {
"login": "octocat",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"body": "Here is the body for the review.",
"state": "APPROVED",
"html_url": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80",
"pull_request_url": "https://api.github.com/repos/octocat/Hello-World/pulls/12",
"_links": {
"html": {
"href": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80"
},
"pull_request": {
"href": "https://api.github.com/repos/octocat/Hello-World/pulls/12"
}
},
"submitted_at": "2019-11-17T17:43:43Z",
"commit_id": "ecdd80bb57125d7ba9641ffaa4d7d2c19d3f3091",
"author_association": "COLLABORATOR"
}
]
owner = 'octocat',
repo = 'hello-world',
pull_number = 42
stub_request(:get, "https://api.github.com/repos/#{owner}/#{repo}/pulls/#{pull_number}/reviews").
to_return(status: 200, body: pull_request_review_response.to_json,
headers: {'Accept'=>'*/*', 'User-Agent'=>'Ruby'})
end
# ....
end
此代码的重要部分是 stub_request
,它采用 HTTP 请求方法、URL 路径,并且 return 将参数传递给 to_return
(作为 HTTP 响应)。响应对象将包含 status
、body
和 header
.
我使用 examples from the WebMock README and the API doc from GitHub 生成了上面的示例(注意: 未经测试)。
现在,您可以定义一个调用 Mock 端点的新测试:
require 'spec_helper'
class CodeReviewSignedOffTest < ActionDispatch::IntegrationTest
test 'GET pull_request_review_response endpoint from GitHub mock' do
uri = URI('https://api.github.com/repos/octocat/hello-world/pulls/42/reviews')
response = Net::HTTP.get(uri)
data = JSON.parse(response&.body)
...
end
end
希望您能看到您在模拟 API 中定义的数据。我还没有测试过这段代码,所以它可能需要一些调试。
如果您有任何问题或 运行 在编写测试时遇到问题,请告诉我!祝你好运。