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.