【问题标题】:How can I unit-test a method that uses a REST API with RSpec?如何对使用带有 RSpec 的 REST API 的方法进行单元测试?
【发布时间】:2016-05-27 18:29:03
【问题描述】:

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

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

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

  • 如果正确,如何使用 RSpec 进行测试?有没有我可以阅读的指南或资料来源?
  • 如果错误,我该如何检查?如果我将 API_VERSION 更改为更高版本,我怎么知道逻辑仍然运行良好,并且所有必填字段仍然存在?

例如:

我有这样的代码:

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

【问题讨论】:

  • 包含SocialNetwork的类定义会更清楚。

标签: ruby rspec tdd automated-tests koala


【解决方案1】:

您说:“我正在尝试对调用外部 REST API 的方法进行 TDD 测试。”对我来说,这是单元测试的圈子(支付的短语是“对方法进行测试”)。 “这是对还是错?”,你问。绝对正确(IMO)。

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

对我来说,这是测试访问外部 API 的方法的最直接、开销最低的方法。如果有兴趣,很乐意多说。

【讨论】:

    【解决方案2】:

    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 分解为小部分(如构建参数、循环等)以提高可测试性。

    【讨论】:

      【解决方案3】:

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

      我会按如下方式测试这段代码:

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

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

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

      【讨论】:

      • 我认为单元测试不适合触及外部或内部服务,这就是为什么我们有模拟、存根、DI 等概念来确保我们隔离单元。然而,对于功能/验收测试,它是适当的或可能是必要的。特别是当你做 TDD 时,你必须为一个函数编写 5 个测试,其中 4 个实际进行 http 调用,至少我可以说很耗时。
      【解决方案4】:

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

      在你的情况下,我认为你可以期待你使用的 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 语法,但我希望它能给你一些想法。

      【讨论】:

        猜你喜欢
        • 2013-07-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-20
        • 2022-11-30
        相关资源
        最近更新 更多