使用 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 方法(使用 stubs)

因此,为了测试名为“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 需要来自 GitHubiterable 对象。假设 code_review_sign_off.pull_request_review return 是数组中的元素数。因为我可以控制 GitHub 的响应,所以我知道只有一个元素会被 returned,所以结果应该是 1,我使用:

断言

assert_equal pull_request_review_response, 1

因此,通常在测试时,您希望隔离您感兴趣的函数并使用 stubs 来保持其他一切不变。

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 响应)。响应对象将包含 statusbodyheader.

我使用 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 中定义的数据。我还没有测试过这段代码,所以它可能需要一些调试。

如果您有任何问题或 运行 在编写测试时遇到问题,请告诉我!祝你好运。