Logo Devoh

Simulating API Responses

I've been working on a project recently which has involved writing an API client for an API I don't actually have access to. This has mostly meant implementing to the documentation provided, but it has also required the development of a simulation service to emulate the responses from the real API while the application is under development.

The client library maps the API responses to domain objects defined with Virtus, so my first thought was to use factory_girl to build the data for the simulator responses. When I started looking into this, I realized that factory_girl is heavily geared towards the testing use case, and as such its definitions are stored into the global FactoryGirl context.

Since the simulation data might be used in an app that has its own factory_girl factories, I didn't want to risk any naming conflicts. It also just seemed wrong to define the simulator factories in that manner. What I wanted was a way to obtain a one-off factory definition without polluting that global namespace.

After poking around in the factory_girl internals a bit, I found that you can get a Factory instance by doing something like this:

factory = FactoryGirl::Factory.new(MyModel)

There isn't a mechanism built into the Factory for defining it, however. The definition is stored on an attribute on the factory, but internally FactoryGirl uses a proxy object to provide a #method_missing DSL that maps method names to attributes on the model.

proxy = FactoryGirl::DefinitionProxy.new(factory.definition)
proxy.instance_eval &block

Once this has been done, the factory can be built using a particular strategy. Since all I'm concerned about here is having the attributes populated, I went with the Build strategy:

factory.run(FactoryGirl::Strategy::Build, overrides, &block)

I ended up encapsulating all this into my own Simulator object, which can be used to define a separate simulator for each domain model.

require 'factory_girl'

class Simulator
  def initialize(model, options={}, &block)
    self.model = model
    self.strategy = options.fetch(:strategy) { FactoryGirl::Strategy::Build }
    self.factory = define(&block)
  end

  def build(overrides={}, &block)
    factory.run(strategy, overrides, &block)
  end

private

  attr_accessor :model
  attr_accessor :strategy
  attr_accessor :factory

  def define(&block)
    FactoryGirl::Factory.new(model).tap do |factory|
      proxy = FactoryGirl::DefinitionProxy.new(factory.definition)
      proxy.instance_eval &block
    end
  end
end

A Simulator can be defined like so:

simulator = Simulator.new(MyModel) do
  value { rand(100) }
end

my_model = simulator.build # => #<MyModel:0x007fcdbc8d40c8 @value=42>

This can then be wrapped up in an object of its own:

class MyModelSimulator < Simulator
  def initialize
    super(MyModel) do
      value { rand(100) }
    end
  end
end

This becomes useful in all sorts of scenarios, including cases where you need to test the consumption of the data returned by the API client but don't want to hit the actual API to do so.

allow_any_instance_of(MyClient)
  .to receive(:my_model)
  .and_return(MyModelSimulator.new.build)

You could also use this to build a simulated version of the API.

# GET /simulator/my_model
def show
  render json: MyModelSimulator.new.build, serializer: MyModelSerializer
end

This approach shows potential, and I believe it merits further investigation. It isn't as reliable as using VCR to record actual server responses, but the ease of setup and the speed and flexibility it affords might be worth the trade-off, since ultimately the responsibility of testing the client-server contract falls on the client library, not the application using it.

and tagged with ruby