Doing some dependency injection and basically want a macro to insert some code if we’re running a spec.
class NeedsTesting
class_property mock_manager : Manager? = nil
property manager : Manager { @@mock_manager || Manager.instance }
end
I am using the class property to inject a mock and otherwise leave the class working as it is.
However I don’t want the overhead of checking for the mock manager in production
So thinking of doing something like
{% begin %}
property manager : Manager { {% if running_spec? %} @@mock_manager || {% end %} Manager.instance }
{% end %}
Best way I can think of to handle this is either use your own compile time flag, or just leverage constructor injection and don’t worry about it.
E.g. something like
module ManagerInterface
# ...
end
class NeedsTesting
@manager : ManagerInterface
def initialize(@manager : ManagerInterface); end
end
# In production
obj = NeedsTesting.new real_manager_you_get_from_somewhere_or_new_up
# In `spec_helper`
class MockManager
include ManagerInterface
# Define additional methods for setting up the obj for each test case.
# ...
end
obj = NeedsTesting.new MockManager.new
I suppose you could try @type.has_constant?("Spec") on the top-level scope. That tells if the Spec module was included (it must be included before this macro expands, though).
Not sure if this is really a good idea, though. I’d probably favor a more direct approach.
You already need to setup the class property somewhere in your spec code. Perhaps it would be easier to apply the default value there as well, for example through reopening the class?
Isn’t it a bit of a code smell to have the class itself have a spec/test only property? Or I assume you’re also monkey patching in the mock_manager class var?
It might be, in other languages. For example in Ruby there’s TimeCop where you can change what Time.now returns. That’s impossible to do in other languages, but it’s fine and even preferred in Ruby. That way you don’t have to pass a Clock interface everywhere.