Ruby rspec mocking a class with an attribute that is a hash - ruby

I have a class I am testing, call it myfoo. It accesses a class called yourbar. Specifically something like this...
yourbar_obj.projects[project_name]
In my spec code I have this
let(:yourbar_obj) { Class.new }
and I want to mock it to respond to the hash attribute access.
So I tried this
expect(yourbar_obj).to receive(projects).and_return(some_obj)
But when I run the code it says
NoMethodError: undefined method `projects' ...
Is it possible to mock a hash access like that? The same type of thing works for regular method calls.
I even tried adding a .with(project_name) just in case. Same error.
thoughts?

Thanks to Max's help. Here is the correct answer...
some_hash_obj[project_name] = some_obj
expect(yourbar_obj).to receive(:projects).and_return(some_hash_obj)
Two key parts. The : before projects, and some_hash_obj must be a hash. I was trying to return the value (which was an obj) at the hash index in one shot, but that ain't how it works. return the hash, and the [] will apply to it.

Related

Ruby rspec mocking a utility method

How does one mock a utility method that is located in a file referenced by
require 'myfile.rb'
The contents of the file are like so.
public
def my_method_name(arg1, arg2, arg3)
{
....
}
At the moment, I have something like:
double("mydouble", "my_method_name" => somehash)
I then unit test the class I am testing, which calls this method, but this does not seem to be mocking the method at all.
All help is appreciated
Basically, the method I want to mock does some network query. I want to just say: Anytime this method is called, return this hash. This method is not part of a class (or so I believe).
In Ruby all methods are associated to something (module or class). Methods that you define at the top level actually become private methods of Kernel.
x = double("mydouble", "my_method_name" => somehash)
Doesn't stub the method my_method_name. It creates a double (separate object). If you invoke #my_method_name on it, it will respond with somehash:
x.my_method_name # => somehash
Find the object the method is being invoked on. If it's easily replaced and doesn't have that much more functions, you can pass this double instead on it's place. If that is not the case, you can stub the method on that object by doing:
said_object.stub(my_method_name: somehash)
If you want to stub it for all instances of a class, you could do:
TheObjectsClass.any_instance.stub(my_method_name: somehash)
Disclaimer: The topic is a bit more complex and subject to debate. Don't consider this as a good testing practice, it just aims to help you understand how to use rspec.
You said this is related to networking. You can consider using VCR to simulate actual requests.
Since the class you are testing is the one on which this method is called, you should be stubbing the method on the same class
allow(<your_class_instance>).to receive(:my_method_name).and_return(<>)
I am assuming this method is an instance method. If its a class method, you will have to stub it at the class level

How does ruby Marshal.load work in the context of object restoration?

Serialization in Ruby can be done via the built-in Marshal module.
It provides methods for dumping an object and loading it.
I'm writing some serialization and wondering how an object can be loaded and all of its attributes restored, without actually calling the constructor?
For example, suppose I have a class
class Test
def initialize(id)
#id = id
end
end
Suppose I serialized it to (assuming a very simplified scheme that likely doesn't work in general)
{
"Test": {
"id": 3
}
}
When I want to load it back, I figured I'll just instantiate a new Test object and set its attributes. However, calling the new method will throw an exception because I didn't pass in an ID yet. Indeed, I haven't gotten to the point where I have read the ID, and in general, a constructor could take any arbitrary number of arguments, and I don't want to have to write custom logic for each and every class.
When you load the object via Marshal.load, it just somehow works. How does it work?
See this answer for an explanation of what the default Class::new does. You can mimic this behavior without adding the call to initialize. Instead, you would manually set the state of the class via something similar to instance_variable_set. Note, this is just a suggesstion of how you can implement this yourself. The actual Marshal.load is likely written in c, but it would do something similar.

Ruby syntax question: Rational(a, b) and Rational.new!(a, b)

Today I came across the strange ruby syntax in the Rational class:
Rational(a,b)
(Notice the absence of the .new()portion compared to the normal Ruby syntax). What does this mean, precisely, compared to the normal new syntax? More importantly, how do I implement something like this in my own code, and why would I implement something like this? Specifically for the Rational class, why is this syntax used instead of the normal instantiation? And why is the new method private in the rational class? (And how/why would I do this in my own ruby code?)
Thanks in advance for your answers, especially since I've asked so many questions.
All you have to do is declare a global function with the same name as your class. And that is what rational.rb does:
def Rational(a, b = 1)
if a.kind_of?(Rational) && b == 1
a
else
Rational.reduce(a, b)
end
end
to make the constructor private:
private :initialize
and similarly for the new method:
private_class_method :new
I suppose Rational.new could be kept public and made to do what Rational() does, but having a method that turns its arguments into instances is consistent with Array(), String(), etc. It's a familiar pattern that's easy to implement and understand.
The method Rational() is actually an instance method defined outside of the class Rational. It therefore becomes an instance method of whatever object loads the library 'rational' (normally main:Object) in the same way that 'puts' does, for example.
By convention this method is normally a constructor for the class of the same name.

Ruby Unit test - Instance variable declared in setUp takes value nil

Hello I have a trouble with Ruby unit testing, I'm new to it so some help would be lovely
class TestItem < Test::Unit::TestCase
def setUp
**#item**=Item.new('Food','Burger',120)
end
def testGetType
assert_equal(**#item**.getType,'Food')
end
end
Here the value of instance variable #item takes nil when I declare it in setUp() and use it in test functions! So I get an error like no method 'getType' for nil-class
But when I directly use it like assert_equal(Item.new('Food','Burger',120).getType,'Food'),it works fine.
Please point out to my mistakes, thanks in advance
The name of the setup method is setup, not setUp. In fact, you will never find a method called setUp in Ruby, since the standard Ruby style for method naming is snake_case, not camelCase. (The same applies to getType and testGetType, BTW. It should be get_type and test_get_type. Well, actually, in Ruby, getters aren't prefixed with get, so really it should be type and test_type. But note that in Ruby, all objects already have type method, although that is deprecated.)

Mocking ActiveRecord relationship beheavior in RSpec tests

I've run into this problem with testing. Let's assume I have two models, User and Post, where user has_many :posts.
I'm trying to spec out a code block that includes something like this:
user = User.find(123)
post = user.posts.find(456)
I know how to mock out the User.find and user.posts parts. The user.posts mock returns an array of Post objects. And when it get's to .find(456) part, everything breaks down with no block given exception.
So my question here is: what do I return as the result of the user.posts mock, so that .find(456) method works on it? User.first.posts.class says it's Array, but obviously there's something more that makes the AR-style find calls work. I'm not overjoyed by the prospect of mocking out find method on the returned object.
PS Before you suggest the obvious and good answer of stop mocking about and using fixtures/seeding the test database with necessary data, here's the catch: legacy scheme. Both User and Post work on top of database views not tables, and changing it so that they are tables in test database seems wrong to me.
The issue is that user.posts isn't actually a simple Array; it's an association proxy object. The way to stub it is probably something like this (though the exact syntax depends on which mocking framework you're using):
def setup
#user = mock(User)
User.stub(:find).with(123).return(#user)
user_posts = mock(Object)
#user.stub(:posts).return(user_posts)
#post = mock(Post)
user_posts.stub(:find).with(456).return(#post)
end
Then in your test, User.find(123) will return #user and #user.posts.find(456) will return #post. If you need #user.posts to act like more of the Array in your tests you can create a mock(Array) and stub the [](index) method.
You could look into the stub_chain method offered by RSpec.
http://apidock.com/rspec/Spec/Mocks/Methods/stub_chain#855-stub-chain-is-very-useful-when-testing-controller-code
Update: Per ryan2johnson9 the updated documentation is : https://relishapp.com/rspec/rspec-mocks/v/3-2/docs/working-with-legacy-code/message-chains

Resources