How to reload a ruby class - ruby

In many of our classes we cache expensive operation for performance. e.g.
def self.foo
#foo ||= get_foo
end
This works great in the application, however the tests (RSpec) fail because of these memoized variables. The values from the first test are being returned in the subsequent tests, when we expect fresh values.
So the question is: how do I reload the class? Or remove all the memoized variables?

Add an after (or before) block to the example group to remove the instance variable (assuming the object in question is the subject):
after do
subject.instance_variable_set(:#foo, nil)
end
Or fix the problem. Having a memoized class instance variable is a bit of a smell since it will never change. Normal instance variables wouldn't have this issue since you'd create a new object for each test.

Build your classes and tests in such a way that the cached data remains correct or gets deleted when it is invalid. Consider adding a method to clear the cache and calling it in a rspec before block.

Related

Test cached (multiprocessed) and memoized method

Short question, I'd like to test this method foo:
class MyClass
def foo
# The `#cache_key` is defined by instance but can be the same for
# different instances (obviously I guess)
#foo ||= cache.fetch(#cache_key) do
# call a private method, tested aside
generate_foo_value || return
# The `|| return` trick is one I use to skip the yield result
# in case of falsey value. May not be related to the question,
# but I left it there because I'm not so sure.
end
end
end
The issue here is that I have two kinds of caching.
On one hand, I have #foo ||= that assure memoization and that I can test pretty easily. For instance by calling twice my method and watch if the result is different.
On the other hand, I have the cache.fetch method which may be shared between different instances. The trouble for me is here: I don't know the best way to unit test it. Should I mock my cache? Should I watch cache result? Or should I run plural instances with the same cache key?
And for this last question, I don't know how to run plural instances easily using rspec.
I don't think you need plural instances in this case. It looks like you could just set the cache value in the specs and test that the MyClass instance(s) is returning it.
before { cache.set(:cache_key, 'this is from cache') }
subject { MyClass.new(:cache_key, cache) } # you didn't provide the
# initializer, so I'll just assume
# that you can set the cache key
# and inject the cache object
specify do
expect(subject.foo).to eq 'this is from cache'
end
You can also set the expectation that generate_foo_value is not run at all in this case.
My reasoning is that: you don't need to fully emulate setting the cache in a separate process. You test only that if the cache with the key is set - your method has to return it instead of doing expensive computation.
The fact that this cache is shared between processes (like it sits in a PostgreSQL DB, or Redis or whatever) is irrelevant for the test.

How to stub method with specific parameter (and leave calls with other parameters unstubbed) in Mocha?

This question may seem like a duplicate of this one but the accepted answer does not help with my problem.
Context
Since Rails 5 no longer supports directly manipulating sessions in controller tests (which now inherit from ActionDispatch::IntegrationTest), I am going down the dark path of mocking and stubbing.
I know that this is bad practice and there are better ways to test a controller (and I do understand their move to integration tests) but I don't want to run a full integration test and call multiple actions in a single test just to set a specific session variable.
Scenario
Mocking/stubbing a session variable is actually quite easy with Mocha:
ActionDispatch::Request::Session.any_instance.stubs(:[]).with(:some_variable).returns("some value")
Problem is, Rails stores a lot of things inside the session (just do a session.inspect anywhere in one of your views) and stubbing the :[] method obviously prevents access to any of them (so session[:some_other_variable] in a test will no longer work).
The question
Is there a way to stub/mock the :[] method only when called with a specific parameter and leave all other calls unstubbed?
I would have hoped for something like
ActionDispatch::Request::Session.any_instance.stubs(:[]).with(:some_variable).returns("some value")
ActionDispatch::Request::Session.any_instance.stubs(:[]).with(anything).returns(original_value)
but I could not find a way to get it done.
By what I see, this is a feature not available in mocha
https://github.com/freerange/mocha/issues/334
I know this does exist in rspec-mock
https://github.com/rspec/rspec-mocks/blob/97c972be57f2c060a4a7fb8a3c5700a5ede693f0/spec/rspec/mocks/stub_implementation_spec.rb#L29
One hacky way that you an do it though, is to store the original session in an object, then mock that whenever a controller receives session, it returns another mock object, and in this you may either return a mocked velue, or delegate the call to the original session
class MySession
def initialize(original)
#original = original
end
def [](key)
if key == :mocked_key
2
else
original[key]
end
end
end
let!(original_session) { controller.send(:session) }
let(:my_session) { MySession.new(original_session) }
before do
controller.stubs(:session) { my_session }
end
Guess that mocha also allows you to do block mocking, so you don't need the class, but you need that original_session to be called
But I don't see a clean way

What object is in play for instance variables (i.e. what is self) in Cucumber step definitions?

I'm not understanding scoping when using Cucumber in Ruby, especially with regard to instance variables.
For the context of my immediate example, in the Before portion of hooks.rb the variable #browser is assigned.
#browser = Watir::Browser.new #browser_selected.to_sym
(where #browser_selected is usually 'chrome')
In step definitions #browser gets used. As a simple example: #browser.send_keys(:tab)
What I don't understand is what object contains #browser as an attribute. How does it have meaning in this context? I know the code I'm puzzled by is always in a block, and I recognize that each such block is used (via the Given/When/Then message to which it is attached) to be pre-processed in some mysterious way.
Amidst this shrouding in mystery is the scoping of instance variables. How can I know the scope of instances variables within such blocks?
self in Cucumber steps and hooks is just a Ruby object, the "world", which is used throughout each scenario. The block in each step definition is executed in the context of the world with the_world.instance_eval or something similar, meaning that when each block runs self is the world. So the object to which all of those instance variables belong is the same object, the world. The scope of all of those instance variables is the entire scenario.
So it's important to use instance variables sparingly in Cucumber steps, and to make it clear in step names that you're using them (that is, make it clear in step names that they refer to some piece of state). These steps clearly refer to a thing which is saved behind the scenes (i.e. refer to the same instance variable):
Given there is a thing
When I frob the thing
Then the thing should be frobbed
That's fine and normal. But it would be terrible if When I frob the thing precalculated some expected assertion results and stashed them in instance variables too, and Then the thing should be frobbed used those instance variables in its assertions. Then the thing should be frobbed would not work unless When I frob the thing preceded it, making it less reusable, and that restriction would not be obvious to others writing features and they would become frustrated. (Don't be like my former colleague.)
Back to the world: Cucumber makes a new world for each scenario and throws it away at the end so a scenario's instance variables don't affect the next scenario. In plain Cucumber, the world is just an instance of Object. In cucumber-rails, it's an instance of Cucumber::Rails::World (which is interesting to read). Aside from the methods built in to the world in cucumber-rails, the world gets its methods by extending modules (as in the_world.extend SomeModule). As with instance variables, all the methods from all the modules that the world extends are jammed on to the same object (the world), so you sometimes need to worry about name conflicts.

Should I open up a class's instance variables just for test validation?

I'm new to BDD, and I'm finding alot of instances where I'm adding instance variables to attr_accessor only so that my tests have an easy way to validate whether they are in the state that they should be in. But this feels a little dirty because no other class needs this information, and so I'm only making it public for the tests. Is this standard or a sign of bad design?
For example I have a collection class which stores an object, and then commits it to a batch array. At the end of the import, the batch array is used to make a batch insert into the database. But there is no need to check on the state of the batch at any point. But in testing I'm wanting to make sure that the batch is in the state that I think it is, and so am opening up that variable for inspection. Is the fact that it's not being checked within the code the real problem?
Is using instance_variable_get an option for you?
>> class Foo
.. def initialize
.. #foo = 'bar'
.. end
.. end #=> nil
>> Foo.new.instance_variable_get(:#foo) #=> "bar"
DON'T Do That!
This is going too deep. See the comments from #Andy and #Michael in the thread on the other 'accepted' answer
Don't write unit tests that look inside the code unit. If it's not something you can see from the external interfaces to the object then it doesn't need to be unit tested. You are going past how the code behaves into how it implements that behavior, and you don't want tests that go down to that level as they don't provide any real value in terms of proving that the code does what it should.
Just consider how many tests you might have to update if someone re-factors that code and in order to make it more readable changes the internal names of things.. or maybe finds a better way of doing whatever and all the instance variables get altered.. The code unit could still be working perfectly fine, but the unit tests would fail..
Alternatively if a change was made that adjusts how the internal variables report out to the interface, the code could effectively be broken, but the tests that are looking at the internal values of the instance variables would not report any failure.

Which is better? Creating a instance variable or passing around a local variable in Ruby?

In general what is the best practice and pro/cons to creating an instance variable that can be accessed from multiple methods or creating an instance variable that is simply passed as an argument to those methods. Functionally they are equivalent since the methods are still able to do the work using the variable. While I could see a benefit if you were updating the variable and wanted to return the updated value but in my specific case the variable is never updated only read by each method to decide how to operate.
Example code to be clear:
class Test
#foo = "something"
def self.a
if #foo == "something"
puts "do #{#foo}"
end
end
a()
end
vs
class Test
foo = "something"
def self.a(foo)
if foo == "something"
puts "do #{foo}"
end
end
a(foo)
end
I don't pass instance variable around. They are state values for the instance.
Think of them as part of the DNA of that particular object, so they'll always be part of what makes the object be what it is. If I call a method of that object, it will already know how to access its own DNA and will do it internally, not through some parameter being passed in.
If I want to apply something that is foreign to the object, then I'll have to pass it in via the parameters.
As you mentioned, this is a non-functional issue about the code. With that in mind...
It's hard to give a definitive rule about it since it depends entirely on the context. Is the variable set once and forgotten about it, or constantly updated? How many methods share the same variable? How will the code be used?
In my experience, variables that drive behavior of the object but are seldom (if at all) modified are set in the initialize method, or given to the method that will cascade behavior. Libraries and leaf methods tend to have the variable passed in, as it's likely somebody will want to call it in isolation.
I'd suggest you start by passing everything first, and then refactoring if you notice the same variable being passed around all over the class.
If I need a variable that is scoped at the instance level, I use an instance variable, set in the initialize method.
If I need a variable that is scoped at the method level (that is, a value that is passed from one method to another method) I create the variable at the method level.
So the answer to your question is "When should my variable be in scope" and I can't really answer that without seeing all of your code and knowing what you plan to do with it.
If your object behavior should be statically set in the initialization phase, I would use an instance variable.

Resources