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

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.)

Related

Ruby: understanding data structure

Most of the Factorybot factories are like:
FactoryBot.define do
factory :product do
association :shop
title { 'Green t-shirt' }
price { 10.10 }
end
end
It seems that inside the ":product" block we are building a data structure, but it's not the typical hashmap, the "keys" are not declared through symbols and commas aren't used.
So my question is: what kind of data structure is this? and how it works?
How declaring "association" inside the block doesn't trigger a:
NameError: undefined local variable or method `association'
when this would happen on many other situations. Is there a subject in compsci related to this?
The block is not a data structure, it's code. association and friends are all method calls, probably being intercepted by method_missing. Here's an example using that same technique to build a regular hash:
class BlockHash < Hash
def method_missing(key, value=nil)
if value.nil?
return self[key]
else
self[key] = value
end
end
def initialize(&block)
self.instance_eval(&block)
end
end
With which you can do this:
h = BlockHash.new do
foo 'bar'
baz :zoo
end
h
#=> {:foo=>"bar", :baz=>:zoo}
h.foo
#=> "bar"
h.baz
#=> :zoo
I have not worked with FactoryBot so I'm going to make some assumptions based on other libraries I've worked with. Milage may vary.
The basics:
FactoryBot is a class (Obviously)
define is a static method in FactoryBot (I'm going to assume I still haven't lost you ;) ).
Define takes a block which is pretty standard stuff in ruby.
But here's where things get interesting.
Typically when a block is executed it has a closure relative to where it was declared. This can be changed in most languages but ruby makes it super easy. instance_eval(block) will do the trick. That means you can have access to methods in the block that weren't available outside the block.
factory on line 2 is just such a method. You didn't declare it, but the block it's running in isn't being executed with a standard scope. Instead your block is being immediately passed to FactoryBot which passes it to a inner class named DSL which instance_evals the block so its own factory method will be run.
line 3-5 don't work that way since you can have an arbitrary name there.
ruby has several ways to handle missing methods but the most straightforward is method_missing. method_missing is an overridable hook that any class can define that tells ruby what to do when somebody calls a method that doesn't exist.
Here it's checking to see if it can parse the name as an attribute name and use the parameters or block to define an attribute or declare an association. It sounds more complicated than it is. Typically in this situation I would use define_method, define_singleton_method, instance_variable_set etc... to dynamically create and control the underlying classes.
I hope that helps. You don't need to know this to use the library the developers made a domain specific language so people wouldn't have to think about this stuff, but stay curious and keep growing.

Understanding Ruby define_method with initialize

So, I'm currently learning about metaprogramming in Ruby and I want to fully understand what is happening behind the scenes.
I followed a tutorial where I included some of the methods in my own small project, an importer for CSV files and I have difficulties to wrap my hand around one of the methods used.
I know that the define_method method in Ruby exists to create methods "on the fly", which is great. Now, in the tutorial the method initialize to instantiate an object from a class is defined with this method, so basically it looks like this:
class Foo
def self.define_initialize(attributes)
define_method(:initialize) do |*args|
attributes.zip(args) do |attribute, value|
instance_variable_set("##{attribute}", value)
end
end
end
end
Next, in an initializer of the other class first this method is called with Foo.define_initialize(attributes), where attributes are the header row from the CSV file like ["attr_1", "attr_2", ...], so the *args are not provided yet.
Then in the next step a loop loops over the the data:
#foos = data[1..-1].map do |d|
Foo.new(*d)
end
So here the *d get passed as the *args to the initialize method respectively to the block.
So, is it right that when Foo.define_initialize gets called, the method is just "built" for later calls to the class?
So I theoretically get a class which now has this method like:
def initialize(*args)
... do stuff
end
Because otherwise, it had to throw an exception like "missing arguments" or something - so, in other words, it just defines the method like the name implies.
I hope that I made my question clear enough, cause as a Rails developer coming from the "Rails magic" I would really like to understand what is happening behind the scenes in some cases :).
Thanks for any helpful reply!
Short answer, yes, long answer:
First, let's start explaining in a really (REALLY) simple way, how metaprogramming works on Ruby. In Ruby, the definition of anything is never close, that means that you can add, update, or delete the behavior of anything (really, almost anything) at any moment. So, if you want to add a method to Object class, you are allowed, same for delete or update.
In your example, you are doing nothing more than update or create the initialize method of a given class. Note that initialize is not mandatory, because ruby builds a default "blank" one for you if you didn't create one. You may think, "what happens if the initialize method already exist?" and the answer is "nothing". I mean, ruby is going to rewrite the initialize method again, and new Foo.new calls are going to call the new initialize.

Accessing instance variables in Ruby OOP

I am learning Ruby OOP and have been faced with the following question.
What could we add to the class below to access the instance variable
#volume?
class Cube
def initialize(volume)
#volume = volume
end
end
My initial thought was to add attr_reader :volume to access the instance variable.
Instead the model answer suggests adding a new method as below.
def get_volume
#volume
end
Why is this the preferred method?
Both methods would output 100 if cube.volume or cube.get_volume were called.
attr_reader. In general methods with get_ prefix are rather avoided in Ruby community (in opposite to commonly seen in Java/C# code)
If it is a dynamically created variable, then you can use instance_vairable_get, like below -
instance_variable_get("#volume")
Since it is desired to get the variable volume ,I will suggest the use of attr_reader :volume. This creates a proxy method volume so no need to add( and later have to maintain) an extra method get_volume for this sole purpose.
https://ruby-doc.org/core-2.1.1/Module.html#method-i-attr_reader
You should use attr_reader :volume instead of using get_volume method.
Stil attr_reader and your method get_volume both are functioning same.
Generally get_ methods should be avoided in ruby.

Dot syntax vs param passing syntax

Are only the core Ruby methods callable using object.functionName syntax? Is it possible to create methods on my own that are callable in the dot syntax fashion?
For this method:
def namechanger (name)
nametochange = name
puts "This is the name to change: #{nametochange}"
end
First one below works, the second does not.
namechanger("Steve")
"Steve".namechanger
I get an error on "Steve".namechanger
The error is:
rb:21:in `<main>': private method `namechanger' called for "Steve":String (NoMethodError)
Yes, you can add methods to the String class to achieve your desired effect; the variable "self" refers to the object which receives the method call.
class String
def namechanger
"This is the name to change: #{self}"
end
end
"Steve".namechanger # => This is the name to change: Steve
This practice is known as monkey patching and should be used carefully.
Instead of monkeypatching, you could alway subclass and thus:
Be precise about what you think the object really is.
Gain all the methods of String
For example:
class PersonName < String
def namechanger
puts "This is the name to change: #{self}"
end
end
s = PersonName.new( "Iain" )
s.namechanger
This is the name to change: Iain
=> nil
What you have here in the first form is a method which takes a parameter, which is very different than a method that doesn't take any parameters. Let me illustrate
ruby
namechanger("Steve")
Looks for a method named namechanger and passes a string argument to it. Straight forward. It looks up in some unknown context, probably the locals of another method which will look it up on the object that receives that method.
ruby
"Steve".namechanger
is a method that takes no arguments that exists on String. Typically these methods use the implicit self parameter to operate on some data.
If you want to be able to call "Steve".namechanger, you have to make namechanger a method of the String class like this:
class String
def namechanger
puts "This is the name to change: #{self}"
end
end
This is generally referred to as "monkey patching" and you might want to improve your general Ruby proficiency a bit before you get into the related considerations and discussions.
You could do
class String
def namechanger
puts "This is the name to change: #{self}"
end
end
The difference is that your first example is a method that's (basically) globally defined, which takes a string and operates on it. This code above however defines a method called "namechanger" which takes no parameters, and defines it directly on the String class. So any and all strings in your application will then have this method.
But as pst said, you should probably not dive into that style of programming until you get a little more familiar with Ruby, so that you can more easily see the upsides and downsides of doing monkeypatching like this. One of the considerations is that you probably have many strings that don't represent names, and it doesn't make a lot of sense for those strings to have a method called namechanger.
That said, if your goal is just to have a little fun with Ruby, to see what you can do, go for it, but remember to be more careful in projects that will have a longer lifespan.

Ruby Testing: Testing for the Absence of a Method

In rspec and similar testing frameworks, how does one test for the absence of a method?
I've just started fiddling with rspec and bacon (a simplified version of rspec.) I wanted to define test that would confirm that a class only allows read access to an instance variable. So I want a class that looks like:
class Alpha
attr_reader :readOnly
#... some methods
end
I am rather stumped:
it "will provide read-only access to the readOnly variable" do
# now what???
end
I don't see how the various types of provided test can test for the absence of the accessor method. I'm a noob in ruby and ruby testing so I'm probably missing something simple.
In Ruby, you can check if an object responds to a method with obj.respond_to?(:method_name), so with rspec, you can use:
Alpha.new.should_not respond_to(:readOnly=)
Alternatively, since classes could override the respond_to? method, you can be stricter and make sure that there is no assignment method by actually calling it and asserting that it raises:
expect { Alpha.new.readOnly = 'foo' }.to raise_error(NoMethodError)
See RSpec Expectations for reference.
I believe you're looking for respond_to?, as in some_object.respond_to? :some_method.

Resources