Interjectable
Interjectable is a really simple Ruby library for dependency injection, designed to make unit testing easier.
Installation
It's a gem!
gem 'interjectable'
Usage
Interjectable has one module (Interjectable) and one method (inject). Use it like so!
class MyClass
extend Interjectable
inject(:dependency) { SomeOtherClass.new }
inject(:other_dependency) { AnotherClass.new }
end
This replaces a pattern we've used before, adding default dependencies in the constructor, or as memoized methods.
# OLD WAY, see above
class MyClass
attr_accessor :dependency
def initialize(dependency=SomeOtherClass.new)
@dependency=dependency
end
def other_depency
@other_dependency ||= AnotherClass.new
end
end
Dependency Injection + Unit Testing?
Ok but what the heck is dependency injection and what does it have to do with unit tests?
So in the real world, objects depend on other objects: object A uses object B to parse a file. This is normal. But what about when you want to test object A independently from object B? Object B might depend on objects C, D, E and so on, so the test would become needlessly complex.
For the sake of testing object A, we can stub out object B with something fake. But for normal usage of object A, we want to actually use object B (and all its dependencies). We can accommodate both these use cases with dependency injection!
Let's check it out: we can build a class A that normally references B, but in our test we can safely replace B, and don't even have to require or load B at all!
# a.rb
class A
extend Interjectable
inject(:b) { B.new }
def read
b.parse
end
end
# a_spec.rb
require 'a'
describe A do
describe "#read" do
before do
a.b = double("FakeB", parse: 'result')
end
it "parses from its b" do
subject.read.should == 'result'
end
end
end
Great, why this library over any other?
Interjectable aims to provide clear defaults for easy debugging.
The other libraries we found used inject/provide setups. That setup is nice because files don't reference each other. However, this can become a headache to debug to actually figure out what is being used where. In our use cases, we use dependency injection for simplified testing, not for hands-free configuration.