Some times when I write unit tests I need to instantiate a class without the initialize
method being invoked. For instance when the constructor instantiates other classes that I will replace with stubs anyway. For instance:
class SomeClassThatIWillTest
def initialize
@client = GoogleAnalyticsClient.new
@cache = SuperAdvancedCacheSystem.new
end
# ...
end
In a test I will probably replace both @client
and @cache
with stubs, so I'd rather the constructor was never invoked. Is there any black magic that can help me out with that?
The initialize method is useful when we want to initialize some class variables at the time of object creation. The initialize method is part of the object-creation process in Ruby and it allows us to set the initial values for an object.
An object instance is created from a class through the process called instantiation. In Ruby this takes place through the Class method new . This function sets up the object in memory and then delegates control to the initialize function of the class if it is present.
The new function in Ruby is used to create a new Enumerator object, which can be used as an Enumerable. Here, Enumerator is an object. Parameters: This function does not accept any parameters.
In Ruby, a method provides functionality to an Object. A class method provides functionality to a class itself, while an instance method provides functionality to one instance of a class. We cannot call an instance method on the class itself, and we cannot directly call a class method on an instance.
Sure you can. Class#new
is nothing more than a convenience method that saves you from having to allocate and initialize an object manually. Its implementation looks roughly like this:
class Class
def new(*args, **kwargs, &blk)
obj = allocate
obj.send(:initialize, *args, **kwargs, &blk)
obj
end
end
You can just call Class#allocate
manually instead, and not call initialize
.
You should not change behavior of tested class in order to unit test it. If your class will have more actions in constructor you will have to mimic it every time. Your test will get tedious to maintain. Replace objects (or even classes) with doubles.
Maybe you could provide already created objects as an arguments to the constructor? It would allow you to use doubles without stubbing new
method on classes.
If you are using rspec, you can:
GoogleAnalyticsClient.stub(new: double)
SuperAdvancedCacheSystem.stub(new: double)
Define your doubles to match expected interface, and voila! No dirty tricks needed.
What about subclassing SomeClassThatIWillTest
and overwriting initialize
in the subclass? No black magic involved ;)
This way you could even call the super
initializer (to test its code, if its more than you showed us), and then alter @client
and @cache
afterwards.
Example of a MiniTest spec using this method:
describe MyTestClass do
subject {
Class.new(MyTestClass) {
def initialize; end
}
}
it "must do something" do
subject.new.do_something.must_equal something
# ...
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With