如何对使用 REST API 和 RSpec 的方法进行单元测试?

How can I unit-test a method that uses a REST API with RSpec?

我正在开发一个从外部 REST API(来自 Facebook、Twitter 或 Instagram 等社交网络)获取数据的项目。

我不确定我所做的是对还是错,所以我需要一些指导。我不知道,当人们创建依赖于外部数据(REST API 或爬行数据)的应用程序时,他们如何使用它进行 TDD。

我的问题是:我正在尝试对调用外部 REST API 的方法进行 TDD 测试。这是对还是错?

例如:

我有这样的代码:

API_VERSION = "v2.5"
FIELD_PAGE_GRAPH = %w(id name picture{url} likes cover is_community_page category link website has_added_app 
  talking_about_count username founded phone mission location is_published description can_post checkins company_overview
  general_info parking hours payment_options access_token
)

FIELD_STREAM_GRAPH = %w(id message story comments.summary(true) likes.summary(true).limit(500) from to link shares created_time
  updated_time type is_published attachments scheduled_publish_time application
)

def self.get_stat_facebook(page_id,access_token=nil)
  graph = Koala::Facebook::API.new(access_token)
  graph.get_objects(page_id.to_s,{:fields => FIELD_PAGE_GRAPH}, {:api_version => API_VERSION})
end

def self.get_feed_facebook(page_id,access_token=nil, options = {})
  options = options.with_indifferent_access
  retry_time = 0
  begin
    graph = Koala::Facebook::API.new(access_token)
    params = {:fields => FIELD_STREAM_GRAPH, :limit => 25}
    params.merge!({:since => options[:_since].to_i}) if options[:_since].present?
    params.merge!({:until => options[:_until].to_i}) if options[:_until].present?
    results = []
    loop do
      graph_response = graph.get_object(page_id.to_s+"/feed", params, {:api_version => API_VERSION})
      break if graph_response.blank?
      results = results+graph_response
      break if options[:_since].blank?
      params[:until] = graph_response.sort_by!{|result| result['created_time']}.first['created_time'].to_time.to_i-1
    end
  rescue Koala::Facebook::ServerError
    sleep 1
    retry_time += 1
    retry if retry_time <= 3
  end
  filter_owner_page(results, page_id)
end

然后我有一个规范

require 'spec_helper'

RSpec.describe SocialNetwork do
  context ".get_stat_facebook" do
    it "when access token is expired"
    it "when access token is not expired"
    it "when page id is not exist"
    it "when page id is exist"
  end

  context ".get_feed_facebook" do
    it "when access token is expired"
    it "when access token is not expired"
    it "when page id is not exist"
    it "when page id is exist"
    it "data contain id field"
    it "data contain message field"
    it "data contain attachment field"
  end
end

在我看来,您想要实现的是更多 functional/acceptance 测试而不是单元测试。我个人认为在单元测试中,您应该隔离单元(方法)并尝试注入所需的依赖项(模拟)并评估函数的输出(期望和断言)。

在你的情况下,我认为你可以期待你使用的 sdk 方法,例如,你有以下方法:

def do_something_with_facebook
  @graph = Koala::Facebook::API.new(oauth_access_token)
end

在那种情况下,我会编写一个测试来检查您的方法是否像下面那样调用 Koala::Facebook::API

def test_method_calls_koalla
    grape = mock
    Koala::Facebook
      .expects(:new)
      .returns(grape)

    method = do_something_with_facebook
  end

它可能不是 rspec 语法,但我希望它能给你一些想法。

是的,测试访问外部服务是合适的,但您可以通过多种方式将其对测试套件的影响降至最低。

我将按如下方式测试此代码:

  • 注册一个Facebook test user.
  • 编写 RSpec 功能规范或 Cucumber 场景,以测试整个堆栈中使用 Facebook 的整个功能,使用测试用户。使用 the VCR gem 记录 Facebook 的响应,这样远程调用就不会减慢您的测试速度。
  • SocialNetwork编写RSpec规范(单元测试)。

    • 在每个对 Facebook 的不同调用的一个或几个单元测试中,让他们使用测试用户访问 Facebook,并再次使用 VCR 来保持他们的速度。或者,让其中的一个或几个一直点击 Facebook,以便您知道 Facebook 的 API 是否发生变化。
    • 在您 SocialNetwork 的其余规范中,那些只是测试您已经测试过的调用变体的规范,去掉 Koala。

    很难准确解释哪些测试应该在 Facebook 上进行,哪些应该使用存根而不让它们出现在我们面前。看看进展如何,如果您需要更多建议,请再次 post。

TDD 用于将您的方法作为一个单元进行测试。来自外部的数据可以是 mocked,因此您可以涵盖每个场景。像

graph = double()
allow(graph).to receive(:get_object).and_return(data)

--

我也要改

context ".get_stat_facebook" do

对于

describe ".get_stat_facebook" do

并使用上下文来描述您要测试的场景。它将提高可读性。

更多:大方法很难测试,因此您可以将 #get_feed_facebook 分成小部分(如构建参数、循环等)以提高可测试性。

你说,"I'm trying to do a TDD test on a method that calls an external REST API." 对我来说,这就是单元测试的环(支付的短语是 'test on a method')。 "Is this right or wrong?",你问。绝对正确(海事组织)。

我当前的项目广泛使用来自多个其他系统的外部 API。我使用 webmock。它给了我很多控制权来确保请求是 well-formed(url、查询、headers 等),让我测试各种响应(成功、权限被拒绝、网络超时等)。而且,管理外部 API 版本很容易。

对我来说,这是最 straight-forward、low-overhead 测试访问外部 API 方法的方法。如果有兴趣,很乐意多说。