toadle
toadle

Reputation: 45

Organizing API-Calls in callbacks

We are maintaining several Rails-Apps which all pose a similar problem that we don't have a really good solution to: All these apps contain models that need to make a API-Call to an external service in their lifecycle.

Possible cases:

What we exprienced to NOT be a good solution: Adding these calls to the after_*callbacks of the model. Since that breaks tests fast, cause all factories now have to deal with the api-calls.

I'm looking for a good way to organize these API-call. How do you guys do this?

Ideas we came up with, which I considered not real ideal:

Upvotes: 0

Views: 644

Answers (2)

toadle
toadle

Reputation: 45

This is how I now did it, after the advise:

In Foo:

class Foo < ActiveRecord::Base
  before_save Foo::DataSync
end

Foo:DataSynclooks like this:

class Foo::DataSync
  def self.before_save(foo)
     ...do the API-Calls...
  end
end

Now for testing in rspec I added this:

To spec_helper.rb:

config.before(:each) do
  Foo::DataSync.stub(:before_save)
end

Note that config.before(:suite) will not work, since Foo:DataSync is not loaded at that time.

Now foo_spec.rb contains just this:

describe Foo do
  let(:foo) {create(:foo)}

  it "will sync its data before every save" do
    expect(Foo::DataSync).to receive(:before_save).with(foo)

    foo.save
  end
end

The Foo::DataSync can be tested like this:

describe Foo::DataSync do
  let!(:foo) {create(:foo)}

  before do
    Foo::DataSync.unstub(:before_save)
  end

  after do
    Foo::DataSync.stub(:before_save)
  end

  describe "#before_save" do
    ...my examples...
  end
end

Upvotes: 1

toro2k
toro2k

Reputation: 19228

If you are concerned about testing you could put the callback methods into a separate class and mock the callback class during testing. Here's an example using RSpec, given the following Foo and FooCallbacks classes:

class Foo < ActiveRecord::Base
  after_save FooCallbacks
end

class FooCallbacks
  def self.after_save
    fail "Call to external API"
  end
end

You can write and successfully run a spec like this:

describe Foo do

  before do
    allow(FooCallbacks).to receive(:after_save)
  end

  it "should not invoke real APIs" do
    Foo.create
  end

end

Upvotes: 2

Related Questions