我如何测试我的 before_save 回调是否做正确的事情

发布于 2024-11-01 04:13:43 字数 554 浏览 1 评论 0原文

我的 ActiveRecord 模型有一个回调,如下所示:

  before_save :sync_to_external_apis

  def sync_to_external_apis                                                                                                 
    [user, assoc_user].each {|cuser|
      if cuser.google_refresh
        display_user = other_user(cuser.id)
        api = Google.new(:user => cuser)
        contact = api.sync_user(display_user)
      end
    }
  end

我想编写一个 rspec 测试来测试调用 save!当 google_refresh 为 true 时,此模型的实例上会导致在新的 Google 实例上调用sync_user。我怎么能这样做呢?

I have a callback on my ActiveRecord model as shown below:

  before_save :sync_to_external_apis

  def sync_to_external_apis                                                                                                 
    [user, assoc_user].each {|cuser|
      if cuser.google_refresh
        display_user = other_user(cuser.id)
        api = Google.new(:user => cuser)
        contact = api.sync_user(display_user)
      end
    }
  end

I would like to write an rspec test which tests that calling save! on an instance of this model causes sync_user to be called on a new Google instance when google_refresh is true. How could I do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

未央 2024-11-08 04:13:43
it "should sync to external apis on save!" do
  model = Model.new
  model.expects(:sync_to_external_apis)
  model.save!
end

顺便说一句,在请求-响应周期中请求不可靠的资源(例如互联网)是一个坏主意。我建议改为创建一个后台作业。

it "should sync to external apis on save!" do
  model = Model.new
  model.expects(:sync_to_external_apis)
  model.save!
end

As an aside, requesting unreliable resources like the internet during the request-response cycle is a bad idea. I would suggest creating a background job instead.

宣告ˉ结束 2024-11-08 04:13:43

通常的测试方法是确保结果符合预期。由于在这种情况下您使用的是 API,这可能会使事情变得复杂。您可能会发现,使用 mocha 创建一个可以发送 API 调用的模拟对象将允许您替换 Google 类,具有同样适合测试目的的东西。

一种更简单但更笨重的方法是使用“测试模式”开关:

def sync_to_external_apis
  [ user, assoc_user ].each do |cuser|
    if (Rails.env.test?)
      @synced_users ||= [ ]
      @synced_users << cuser
    else
      # ...
    end
  end
end

def did_sync_user?(cuser)
  @synced_users and @synced_users.include?(cuser)
end

这是一种简单的方法,但它不会验证您的 API 调用是否正确。

The usual method for testing is to ensure the results are as expected. Since you're using an API in this case that may complicate things. You may find that using mocha to create a mock object you can send API calls would allow you to substitute the Google class with something that works just as well for testing purposes.

A simpler, yet clunkier approach is to have a "test mode" switch:

def sync_to_external_apis
  [ user, assoc_user ].each do |cuser|
    if (Rails.env.test?)
      @synced_users ||= [ ]
      @synced_users << cuser
    else
      # ...
    end
  end
end

def did_sync_user?(cuser)
  @synced_users and @synced_users.include?(cuser)
end

This is a straightforward approach, but it will not validate that your API calls are being made correctly.

九歌凝 2024-11-08 04:13:43

摩卡是必经之路。我不熟悉 rspec,但这就是您在测试单元中执行此操作的方式:

def test_google_api_gets_called_for_user_and_accoc_user
    user = mock('User')                  # define a mock object and label it 'User'
    accoc_user = mock('AssocUser')       # define a mock object and label it 'AssocUser'

    # instantiate the model you're testing with the mock objects
    model = Model.new(user, assoc_user)

    # stub out the other_user method.  It will return cuser1 when the mock user is 
    # passed in and cuser2 when the mock assoc_user is passed in
    cuser1 = mock('Cuser1')
    cuser2 = mock('Cuser2')
    model.expects(:other_user).with(user).returns(cuser1)
    model.expects(:other_user).with(assoc_user).returns(cuser2)


    # set the expectations on the Google API
    api1 - mock('GoogleApiUser1')          # define a mock object and lable it 'GoogleApiUser1'
    api2 - mock('GoogleApiUser2')          # define a mock object and lable it 'GoogleApiUser2'

    # call new on Google passing in the mock  user and getting a mock Google api object back
    Google.expects(:new).with(:user => cuser1).returns(api1)
    api1.expects(:sync_user).with(cuser1)

    Google.expects(:new).with(:user => cuser2).returns(api2)
    api2.expects(:sync_user).with(cuser2)

    # now execute the code which should satisfy all the expectations above
    model.save!
end

上面的内容可能看起来很复杂,但一旦您掌握了窍门,就不是这样了。您正在测试,当您调用 save 时,您的模型会执行它应该执行的操作,但您不会遇到真正与 API 对话、实例化数据库记录等的麻烦或时间消耗。

Mocha is the way to go. I'm not familiar with rspec, but this is how you would do it in test unit:

def test_google_api_gets_called_for_user_and_accoc_user
    user = mock('User')                  # define a mock object and label it 'User'
    accoc_user = mock('AssocUser')       # define a mock object and label it 'AssocUser'

    # instantiate the model you're testing with the mock objects
    model = Model.new(user, assoc_user)

    # stub out the other_user method.  It will return cuser1 when the mock user is 
    # passed in and cuser2 when the mock assoc_user is passed in
    cuser1 = mock('Cuser1')
    cuser2 = mock('Cuser2')
    model.expects(:other_user).with(user).returns(cuser1)
    model.expects(:other_user).with(assoc_user).returns(cuser2)


    # set the expectations on the Google API
    api1 - mock('GoogleApiUser1')          # define a mock object and lable it 'GoogleApiUser1'
    api2 - mock('GoogleApiUser2')          # define a mock object and lable it 'GoogleApiUser2'

    # call new on Google passing in the mock  user and getting a mock Google api object back
    Google.expects(:new).with(:user => cuser1).returns(api1)
    api1.expects(:sync_user).with(cuser1)

    Google.expects(:new).with(:user => cuser2).returns(api2)
    api2.expects(:sync_user).with(cuser2)

    # now execute the code which should satisfy all the expectations above
    model.save!
end

The above may seem complicated, but it's not once you get the hang of it. You're testing that when you call save, your model does what it is supposed to do, but you don't have the hassle, or time expense of really talking to APIs, instantiating database records, etc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文