Rspec - Stubbing gets.chomp out of the constructor - ruby

I'm writing a Mastermind game in Ruby and in the constructor of the class 'Game' I want to use gets.chomp to ask the user for its name. Pretty easy, but where I run into trouble is when testing this class in RSpec, but I can't seem to properly stub out 'gets' and 'puts', because they are in the constructor not a regular method.
class Game
def initialize
puts "Please enter your name:"
#player = Player.new(gets.chomp)
end
end
describe Game do
Game.stub(:gets).and_return("Create Code AI")
Game.stub(:puts)
subject(:game) { Game.new }
describe "#new" do
its("player.name") { eql("Create Code AI") }
end
end
class Player
attr_reader :name
def initialize(name)
#name = name
end
end
I've also tried putting the stubs into 'before' and 'let' blocks amongst other things, but nothing seems to work. Any help is appreciated!

I have a method which captures stdin and stdout to help with the testing in cases like these:
require 'stringio'
module Kernel
def capture_stdout(console_input = '')
$stdin = StringIO.new(console_input)
out = StringIO.new
$stdout = out
yield
return out.string.strip
ensure
$stdout = STDOUT
$stdin = STDIN
end
end
Now, let's assume I want to test a method that interacts with stdin/stdout:
def greet
name = gets
puts "Welcome, #{name}!"
end
I would write the following test:
require 'rspec/autorun'
RSpec.describe '#say_name' do
it 'prints name correctly' do
input = 'Joe'
result = capture_stdout(input) do
greet
end
expect(result).to eql 'Welcome, Joe!'
end
end
I presented the example above to illustrate how to test both console input and output.
In your case, the test could look like this:
describe Game do
subject(:game) do
capture_stdout('Create Code AI') { return Game.new }
end
describe "#new" do
its("player.name") { eql("Create Code AI") }
end
end
Note: In order for this to work, #player should be an accessible member of Game. So, you may want to add this to your Game class:
attr_reader :player

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

RSpec: How to mock an object and methods that take parameters

I'm writing RSpec unit tests for a CommandLineInterface class that I've created for my Directory object. The CommandLineInterface class uses this Directory object to print out a list of people in my Directory. Directory has a #sort_by(param) method that returns an array of strings. The order of the strings depends on the param passed to the #sort_by method (e.g., sort_by("gender"). What would be the correct way to mock out this Directory behavior in my CLI specs? Would I use an instance_double? I am not sure how to do this for a method that takes parameters, like sorting by gender.
I'm only using Ruby and RSpec. No Rails, ActiveRecord, etc. being used here.
Snippets from the class and method I want to mock out:
class Directory
def initialize(params)
#
end
def sort_by(param)
case param
when "gender" then #people.sort_by(&:gender)
when "name" then #people.sort_by(&:name)
else raise ArgumentError
end
end
end
It all depends on how your objects are collaborating.
Some information is lacking in your question:
How does CommandLineInterface use Directory? Does it create an instance by itself or does it receive one as an argument?
Are you testing class methods or instance methods? (Prefer instance methods)
Here's how you could do it if you pass in the dependent object:
require 'rspec/autorun'
class A
def initialize(b)
#b = b
end
def foo(thing)
#b.bar(thing)
end
end
RSpec.describe A do
describe '#foo' do
context 'when given qux' do
let(:b) { double('an instance of B') }
let(:a) { A.new(b) }
it 'calls b.bar with qux' do
expect(b).to receive(:bar).with('qux')
a.foo('qux')
end
end
end
end
If the class initializes the dependant object and it isn't important to know which instance got the message you can do this:
require 'rspec/autorun'
B = Class.new
class A
def initialize
#b = B.new
end
def foo(thing)
#b.bar(thing)
end
end
RSpec.describe A do
describe '#foo' do
context 'when given qux' do
let(:a) { A.new }
it 'calls b.bar with qux' do
expect_any_instance_of(B).to receive(:bar).with('qux')
a.foo('qux')
end
end
end
end
If you just want to stub out the return value and not test whether the exact message was received, you can use allow:
require 'rspec/autorun'
B = Class.new
class A
def initialize
#b = B.new
end
def foo(thing)
thing + #b.bar(thing)
end
end
RSpec.describe A do
describe '#foo' do
context 'when given qux' do
let(:a) { A.new }
it 'returns qux and b.bar' do
allow_any_instance_of(B).to receive(:bar).with('qux') { 'jabber' }
expect(a.foo('qux')).to eq('quxjabber')
end
end
end
end

How to run a method from another class in Ruby

Hi I try to make my first game in ruby :)
I have two files:
#"game.rb" with code:
class Game
attr_accessor :imie, :klasa, :honor
def initialize(start_scena)
#start = start_scena
end
def name()
puts "Some text"
exit(0)
end
end
and second file
#"game_engine.rb"
require_relative 'game.rb'
class Start
def initialize
#game = Game.new(:name)
end
def play()
next_scena = #start
while true
puts "\n---------"
scena = method(next_scena)
next_scena = scena.call()
end
end
end
go = Start.new()
go.play()
The question is, how can I call class Game.name method from Start.play() class. The game goes deeper, and insted of 'exit(0)' it returns :symbol of another method from "Game" class that should work.
Make start readable for the Game class. DO NOT call exit(0) in your code unless it's really necessary. Instead, use some conditions to make sure the program runs to the end of script.
#"game.rb" with code:
class Game
attr_accessor :imie, :klasa, :honor
attr_reader :start
def initialize(start_scena)
#start = start_scena
end
def name()
puts "Some text"
:round2
end
def round2
puts "round2"
nil
end
end
Use instance#method(...) to get a bounded method to that instance.
#"game_engine.rb"
require_relative 'game.rb'
class Start
def initialize
#game = Game.new(:name)
end
def play()
next_scene = #game.start
while next_scene
puts "\n---------"
scene = #game.method(next_scene)
next_scene = scene.call()
end
end
end
go = Start.new()
go.play()

How do I test a function with gets.chomp in it?

I have a simple function using gets.chomp like this:
def welcome_user
puts "Welcome! What would you like to do?"
action = gets.chomp
end
I'd like to test it using ruby's built in TestCase suite like this:
class ViewTest < Test::Unit::TestCase
def test_welcome
welcome_user
end
end
The problem is, when I run that test, the gets.chomp stops the test because it needs the user to enter in something. Is there a way I can simulate user inputs using just ruby?
You could create a pipe and assign its "read end" to $stdin. Writing to the pipe's "write end" then simulates user input.
Here's an example with a little helper method with_stdin for setting up the pipe:
require 'test/unit'
class View
def read_user_input
gets.chomp
end
end
class ViewTest < Test::Unit::TestCase
def test_read_user_input
with_stdin do |user|
user.puts "user input"
assert_equal(View.new.read_user_input, "user input")
end
end
def with_stdin
stdin = $stdin # remember $stdin
$stdin, write = IO.pipe # create pipe assigning its "read end" to $stdin
yield write # pass pipe's "write end" to block
ensure
write.close # close pipe
$stdin = stdin # restore $stdin
end
end
You first separate the 2 concerns of the method:
def get_action
gets.chomp
end
def welcome_user
puts "Welcome to Jamaica and have a nice day!"
action = get_action
return "Required action was #{action}."
end
And then you test the second one separately.
require 'minitest/spec'
require 'minitest/autorun'
describe "Welcoming users" do
before do
def get_action; "test string" end
end
it "should work" do
welcome_user.must_equal "Required action was test string."
end
end
As for the first one, you can
Test it by hand and rely that it won't break (recommended approach, TDD is not a religion).
Get the subverted version of the shell in question and make it imitate the user, and compare
whether get_action indeed gets what the user types.
While this is a practical answer to your problem, I do not know how to do 2., I only know how to imitate the user behind the browser (watir-webdriver) and not behind the shell session.
You could inject the IO dependency. gets reads from STDIN, which is class IO. If you inject another IO object into your class, you can use StringIO in your tests. Something like this:
class Whatever
attr_reader :action
def initialize(input_stream, output_stream)
#input_stream = input_stream
#output_stream = output_stream
end
def welcome_user
#output_stream.puts "Welcome! What would you like to do?"
#action = get_input
end
private
def get_input
#input_stream.gets.chomp
end
end
Tests:
require 'test/unit'
require 'stringio'
require 'whatever'
class WhateverTest < Test::Unit::TestCase
def test_welcome_user
input = StringIO.new("something\n")
output = StringIO.new
whatever = Whatever.new(input, output)
whatever.welcome_user
assert_equal "Welcome! What would you like to do?\n", output.string
assert_equal "something", whatever.action
end
end
This allows your class to interact with any IO stream (TTY, file, network, etc.).
To use it on the console in production code, pass in STDIN and STDOUT:
require 'whatever'
whatever = Whatever.new STDIN, STDOUT
whatever.welcome_user
puts "Your action was #{whatever.action}"

Executing code for every method call in a Ruby module

I'm writing a module in Ruby 1.9.2 that defines several methods. When any of these methods is called, I want each of them to execute a certain statement first.
module MyModule
def go_forth
a re-used statement
# code particular to this method follows ...
end
def and_multiply
a re-used statement
# then something completely different ...
end
end
But I want to avoid putting that a re-used statement code explicitly in every single method. Is there a way to do so?
(If it matters, a re-used statement will have each method, when called, print its own name. It will do so via some variant of puts __method__.)
Like this:
module M
def self.before(*names)
names.each do |name|
m = instance_method(name)
define_method(name) do |*args, &block|
yield
m.bind(self).(*args, &block)
end
end
end
end
module M
def hello
puts "yo"
end
def bye
puts "bum"
end
before(*instance_methods) { puts "start" }
end
class C
include M
end
C.new.bye #=> "start" "bum"
C.new.hello #=> "start" "yo"
This is exactly what aspector is created for.
With aspector you don't need to write the boilerplate metaprogramming code. You can even go one step further to extract the common logic into a separate aspect class and test it independently.
require 'aspector'
module MyModule
aspector do
before :go_forth, :add_multiply do
...
end
end
def go_forth
# code particular to this method follows ...
end
def and_multiply
# then something completely different ...
end
end
You can implement it with method_missing through proxy Module, like this:
module MyModule
module MyRealModule
def self.go_forth
puts "it works!"
# code particular to this method follows ...
end
def self.and_multiply
puts "it works!"
# then something completely different ...
end
end
def self.method_missing(m, *args, &block)
reused_statement
if MyModule::MyRealModule.methods.include?( m.to_s )
MyModule::MyRealModule.send(m)
else
super
end
end
def self.reused_statement
puts "reused statement"
end
end
MyModule.go_forth
#=> it works!
MyModule.stop_forth
#=> NoMethodError...
You can do this by metaprogramming technique, here's an example:
module YourModule
def included(mod)
def mod.method_added(name)
return if #added
#added = true
original_method = "original #{name}"
alias_method original_method, name
define_method(name) do |*args|
reused_statement
result = send original_method, *args
puts "The method #{name} called!"
result
end
#added = false
end
end
def reused_statement
end
end
module MyModule
include YourModule
def go_forth
end
def and_multiply
end
end
works only in ruby 1.9 and higher
UPDATE: and also can't use block, i.e. no yield in instance methods
I dunno, why I was downvoted - but a proper AOP framework is better than meta-programming hackery. And thats what OP was trying to achieve.
http://debasishg.blogspot.com/2006/06/does-ruby-need-aop.html
Another Solution could be:
module Aop
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def before_filter(method_name, options = {})
aop_methods = Array(options[:only]).compact
return if aop_methods.empty?
aop_methods.each do |m|
alias_method "#{m}_old", m
class_eval <<-RUBY,__FILE__,__LINE__ + 1
def #{m}
#{method_name}
#{m}_old
end
RUBY
end
end
end
end
module Bar
def hello
puts "Running hello world"
end
end
class Foo
include Bar
def find_hello
puts "Running find hello"
end
include Aop
before_filter :find_hello, :only => :hello
end
a = Foo.new()
a.hello()
It is possible with meta-programming.
Another alternative is Aquarium. Aquarium is a framework that implements Aspect-Oriented Programming (AOP) for Ruby. AOP allow you to implement functionality across normal object and method boundaries. Your use case, applying a pre-action on every method, is a basic task of AOP.

Resources