how to rspec test a class ##var value from ENV - ruby

I have a class that has a class variable ##var getting its value based on an ENV setting. and a class method uses this ##var to perform a certain task.
However, I can't seem to be able to find a way to test this class method for the case where the ENV is set to the desired value.
Here are the code for the simplified class and spec test
class Doer
##master = (ENV.has_key?('MASTER') && ENV['MASTER'] == 'yes')
def self.check_master
##master
end
end
require_relative '../bin/doer'
describe Doer do
describe '#check_master' do
context 'env init' do
it 'is set to yes' do
stub_const('ENV', {'MASTER' => 'yes'})
expect(Doer.check_master).to eq(true)
end
it 'is set to other value' do
stub_const('ENV', {'MASTER' => 'no'})
expect(Doer.check_master).to eq(false)
end
it 'is NOT set or unavailable' do
expect(Doer.check_master).to eq(false)
end
end
end
end

Related

Rspecs with ruby file

I have a ruby file airplane.rb
with a ruby class like so -
class AirplaneSeat
attr_accessor :seat_row, :seat_column, :type, :order, :assigned_passenger
def initialize(seat_row, seat_column, type, order, assigned_passenger = 0)
#seat_row = seat_row
#seat_column = seat_column
#type = type
#order = order
#assigned_passenger = assigned_passenger
end
def get_passenger_seating
#some code
end
end # end of class
# outside the class
begin
puts "Enter the seating matrix as 2D array"
seat_matrix = JSON.parse gets.chomp
puts "Enter the number of passengers"
no_of_passengers = gets.chomp
raise "Please enter a valid passenger count" if (no_of_passengers.empty? || no_of_passengers.to_i <=0)
AirplaneSeat.get_passenger_seating(seat_matrix, no_of_passengers)
rescue Exception => e
puts "Error encountered -> #{e.message}"
end
So the ruby class has a few methods and couple of lines of code to execute outside the class, which takes input from the user and then calls the class method.
How do I go about writing test cases for this? I have the rspecs gem and spec folder setup done.
I don't really understand how to begin with the test cases.
Any pointers greatly appreciated.
Thanks!
As a simple example say we have our file for a Foo class, foo.rb:
class Foo
def call
'bar'
end
end
We can create a spec, foo_spec.rb:
require 'rspec'
require_relative 'foo'
RSpec.describe Foo do
describe '#call' do
it 'works' do
expect(described_class.new.call).to eq 'Bar'
end
end
end
And then from the command line we can run the spec:
$ rspec foo_spec.rb

What's best practice for mocking new method on different class

I have next scenario:
module Module
class CommandPattern
def initialize(value)
command = []
#var = value['something']
#abc = value['abc']
#command << value
end
def add(value)
#command << value
end
def get_command
#command
end
end
end
module Module
class Implementator
def initialize(value)
#value = value
end
def method_to_test(argument)
var = "command1"
cmd = CommandPattern.new(var)
var2 = "command2"
cmd.add(var2)
var3 = argument
cmd.add(var3)
commands = var + var2 + var3
commands
end
end
end
So, when I'm testing Module::B.method_I_want_to_test, what would be the best practice to mock "var = A.new(some_stuff)"? Beside refactoring and moving this line into separate method, is there some nice way to do this?
Little bit of background on this question - this style (Module::ClassA and Module::ClassB) - I'm using http://naildrivin5.com/gli/ and reason for this approach is that class A is actually implementing Command Pattern.
So issue I was apparently getting was due to wrong way of trying to write specs.
What I did before was (on the way how #spickermann advised):
RSpec.describe Module::Implementator do
describe "#method_to_test" do
let(:command_argument) { "command" }
let(:cmnd) { double(CommandPattern, :new => command_argument, :add => command_argument)}
subject(:method_to_test) do
Implementator.new("value").method_to_test("dejan")
end
before do
allow(CommandPattern).to receive(:new).with(any_args).and_return(cmnd)
allow(CommandPattern).to receive(:add).with(any_args).and_return(cmnd)
end
it 'does something' do
expect{ method_to_test }.not_to raise_error
end
it 'does something else' do
result = method_to_test
expect(result).to eq("command1command2dejan")
end
end
end
Issue was apparently in testing Module::Implementator, didn't realise I can put module around my RSpec.describe block and solve my first issue:
module Module
RSpec.describe Implementator do
describe "#method_to_test" do
let(:command_argument) { "command" }
let(:cmnd) { double(CommandPattern, :new => command_argument, :add => command_argument)}
subject(:method_to_test) do
Implementator.new("value").method_to_test("dejan")
end
before do
allow(CommandPattern).to receive(:new).with(any_args).and_return(cmnd)
allow(CommandPattern).to receive(:add).with(any_args).and_return(cmnd)
end
it 'does something' do
expect{ method_to_test }.not_to raise_error
end
it 'does something else' do
result = method_to_test
expect(result).to eq("command1command2dejan")
end
end
end
end
Another issue I had was global variable keeping YAML structure, which I missed to see and declare in spec_helper.rb
However, thank's to #spickermann's advices, issue is solved.
I would start with something like this:
describe '#method_I_want_to_test' do
let(:something) { # whatever something needs to be }
let(:a) { double(A, # methods you need from a) }
subject(:method_I_want_to_test) do
B.new(something).method_I_want_to_test
end
before do
allow(A).to receive(:new).with(something).and_return(a)
end
it 'does what I expect' do
expect(method_I_want_to_test).to eq(# what do_semething_else returns)
end
end
The interesting part is the before block that stubs the new method on A. It returns always the double defined in the let(:a) line instead of a real instance of A

Dynamic properties in ruby class

How can I create a dynamic property in ruby? This functionality exists in python.
class Example(object):
value = "This is property!"
class Test(object):
#property
def check(self):
return Example
test = Test()
print(test.check.value) # This is property!
How can I do the same in ruby?
class Example
def value
"This is property!"
end
end
class Test
def check
Example.new
end
end
test = Test.new
puts test.check.value # This is property!
class Test
def check
"This is property!"
end
end
test = Test.new
puts(test.check) # This is property!
Not sure what you want from your example. Properties (from what I've seen) are usually used to create setters and getters. You can have that in Ruby with attr_accessor:
class Test
attr_accessor :check
end
You can call attr_accessor anytime you want an attribute:
class Test
%w{this are possible attribute names}.each do |att|
attr_accessor att
end
end
Or
Class Test
end
test = Test.new
Test.send(:attr_accessor, :whatever)
test.whatever = "something"
test.whatever # => "something"
If you only want a getter you have attr_reader, and there's attr_writer for a writer. They all, for an attribute called attribute_name, use an instance variable called #attribute_name. They all may be built with instance_variable_set and instance_variable_get, which allow dynamically setting and getting instance variables.
You can use ruby's method_missing to achieve something similar:
class TestCheck
def method_missing(methodId)
if(methodId.id2name == "check")
puts "check called"
else
puts "method not found"
end
end
end
t = TestCheck.new
t.check #=> "check called"
t.something_else #=> "method not found"
Reference: Ruby docs

RSpec lazy subject

When testing class methods, I don't need an instance to be created automatically. Is an implicit subject created automatically, or only when referenced?
describe MyClass do
it 'uses implicit subject' do
subject.my_method.should be_true
end
it 'does not create a subject' do
MyClass.works?.should be_true
# subject should not have been created
end
end
subject appears to be a method which creates the object necessary and returns it. So it would only create a subject object when called.
It's easy enough to test yourself though...
class MyClass
cattr_accessor :initialized
def initialize
MyClass.initialized = true
end
def my_method
true
end
def self.works?
true
end
end
describe MyClass do
it 'uses implicit subject' do
MyClass.initialized = false
subject.my_method.should be_true
MyClass.initialized.should == true
end
it 'does not create a subject' do
MyClass.initialized = false
MyClass.works?.should be_true
MyClass.initialized.should == false
end
end
Those specs pass, proving that it's lazy.

RSpec - spec fails when trying to call method on initialized attribute

Here is the code for my specs and class:
describe Game do
before(:each) do
#game = Factory.build(:game)
end
describe '#no_books?' do
it 'should return true if books attribute is empty' do
#game.stub(:books).and_return([])
#game.no_books?.should be_true
end
it 'should return false if books attribute is present' do
#game.no_books?.should be_false
end
end
end
class Game
attr_reader :books
def initialize
#books = parse_books
end
def no_books?
#books.empty?
end
protected
def parse_books
# return books
end
end
I then get a friendly spec failure message:
Game#no_books? should return true if books attribute is empty
Failure/Error: #game.no_books?.should be_true
expected false to be true
It's as if that method is getting called before the attribute books get initialized with a value. Can someone explain to me what's going here?
Your no_books? implementation is using the instance variable directly in its check, bypassing your stub. If you change no_books? to return books.empty?, it will call the stub instead.
If you really, really want to keep using the instance variable, you can set it in #game via instance_variable_set like this:
#game.instance_variable_set("#books", [])

Resources