RSpec test for a module - ruby

I'm brand new to RSpec and TDD. I was wondering if someone might help me with creating a test well-suited for this Module:
module Kernel
# define new 'puts' which which appends "This will be appended!" to all puts output
def puts_with_append *args
puts_without_append args.map{|a| a + "This will be appended!"}
end
# back up name of old puts
alias_method :puts_without_append, :puts
# now set our version as new puts
alias_method :puts, :puts_with_append
end
I'd like for my test to check that the content from a 'puts' ends with "This will be appended!". Would that be a sufficient test? How would I do that?

The best tests test what you're trying to achieve, not how you achieve it... Tying tests to implementation makes your tests brittle.
So, what you're trying to achieve with this method is a change to "puts" whenever your extension is loaded. Testing the method puts_with_append doesn't achieve this goal... If you later accidentally re-alias that to something else, your desired puts change won't work.
However, testing this without using an implementation detail would be rather difficult, so instead, we can try to push the implementation details down to somewhere they won't change, like STDOUT.
Just the Test Content
$stdout.stub!(:write)
$stdout.should_receive(:write).with("OneThis will be appended!")
puts "One"
Full Test
I'm going to turn this into a blog post within the next day or so, but I think you should also consider that you've got a desired result for one and many arguments, and your tests should be easy to read. The ultimate structure I'd use is:
require "rspec"
require "./your_extention.rb"
describe Kernel do
describe "#puts (overridden)" do
context "with one argument" do
it "should append the appropriate string" do
$stdout.stub!(:write)
$stdout.should_receive(:write).with("OneThis will be appended!")
puts "One"
end
end
context "with more then one argument" do
it "should append the appropriate string to every arg" do
$stdout.stub!(:write)
$stdout.should_receive(:write).with("OneThis will be appended!")
$stdout.should_receive(:write).with("TwoThis will be appended!")
puts("One", "Two")
end
end
end
end

Related

In RSpec, Is there a way to get information about the example to be tested for use in before(:each)?

I would like to keep track at runtime of the RSpec test currently being executed, preferably including its file name and line number, so that if a test hangs I can know where to find it. I'm thinking of simply writing the file name and line number to a file in a before(:each).
I realize I can see in the output the most recently executed test, but I would like to know the currently executing test.
before(:each) yields an RSpec::Core::Example instance representing the current test/example, which should contain the information you're after.
before(:each) do |example|
puts example.description
puts example.location
puts example.metadata
end

Augmenting the methods with `alias` keyword

Having reading through The Ruby programming language I found an example of using alias keyword for augmenting the methods.
def hello # A nice simple method
puts 'Hello world' # Suppose we want to augment it...
end
alias original_hello hello # Give the method a backup name
def hello # Now we define a new method with the old name
puts "Your attention please" # That does some stuff
original_hello # Then calls the original method
puts "This has been a test" # Then does some more stuff
end
Indeed original hello preserves the old behavior even after the method the it had been referencing to was redefined.
But, to my mind, this example hardly clarifies the real benefit of this technique. Cannot the same be achieved in traditional way (e.g. by providing the block)? Then why applying this idiom? Can anyone provide an example from the real world when augmenting with alias really makes sense?
Rails code is full of those. Imagine the original hello method does not belong to your code base. Somewhere in 3rd-party library there is do_stuff(stuff) method declared on the class Stuffer.
You want to e.g. debug this method. You reopen the class, define an alias and, voilà:
class Stuffer
alias original_do_stuff do_stuff
def do_stuff(stuff)
puts stuff.inspect
original_do_stuff(stuff)
end
end
Now all the code, including original 3rd party code you might be even not aware about, would print out the parameter passed to every single call to do_stuff.
Real-life example (don’t try this at home and in the school :)
class String
alias _inspect inspect
def inspect
puts "I am a string: “#{_inspect}”"
end
end
"abc".inspect
#⇒ I am a string: “"abc"”
Can anyone provide an example from the real world when augmenting with alias really makes sense?
Not really. Today, you would do this (example taken from #mudasobwa's answer):
module WeirdInspectRefinement
module WeirdInspectExtension
def inspect
"I am a string: “#{super}”"
end
end
refine String do
prepend WeirdInspectExtension
end
end
using WeirdInspectRefinement
p 'abc'.inspect
#⇒ 'I am a string: “"abc"”'
But even before Module#prepend and Refinements existed, there was never a reason to use alias for this, which leaves unused methods around polluting the namespace, and Rails abandoned it quite a while ago:
class String
old_inspect = instance_method(:inspect)
define_method(:inspect) do
"I am a string: “#{old_inspect.bind(self).()}”"
end
end
'abc'.inspect
#⇒ 'I am a string: “"abc"”'

First time unit testing with rspec in ruby

I am just trying to understand the basics behind unit testing. I wrote a Player class in a file called Player.rb. Here is the code:
Class Player
attr_reader :health
attr_accessor :name
def initialize(name, health=100)
#name = name.capitalize
#health = health
end
def blam
#health -= 10
puts "#{#name} just got blammed yo."
end
def w00t
#health += 15
puts "#{#name} just got w00ted."
end
def score
#health + #name.length
end
def name=(new_name)
#name = new_name.capitalize
end
def to_s
puts "I'm #{#name} with a health of #{#health} and a score of #{score}"
end
end
Here is my spec file:
require_relative 'Player'
describe Player do
it "has a capitalized name" do
player = Player.new("larry", 150)
player.name.should == "Larry"
end
end
Does that seem about right? My question is in regards to the syntax of the spec file. I understand why I need to require the Player class. But what is the describe and it sections of the code doing? Why do I need the it section? All it seems to be doing is defining a string right?
Finally, when I run rspec player_spec.rb from Terminal, I get this warning:
Deprecation Warnings:
Using `should` from rspec-expectations' old `:should` syntax without explicitly enabling the syntax is deprecated. Use the new `:expect` syntax or explicitly enable `:should` instead. Called from /Users/Jwan/studio_game/player_spec.rb:7:in `block (2 levels) in <top (required)>'.
What does the above warning mean? Do I have to replace should with enable syntax? How do I enable the :should syntax? Why is :should written as a symbol?
Yes, that seems about right. One thing you might find useful is to use subject, which lets you define a "subject" to use in multiple tests:
describe Player do
subject { Player.new("larry", 150) }
it "has a capitalized name" do
expect(subject.name).to eq "Larry"
end
end
This way you don't have to define player over and over again in every test—subject will automatically initialize it for you each time.
describe and it are primarily for organization. A large project will ultimately have thousands of tests, and a change in one class might cause a test to fail for a completely different part of application. Keeping tests organized makes it much easier to find and fix errors as they occur.
As for your warning, it looks like you're using an old guide or tutorial that tells you to use "should" syntax, but in RSpec 3 this syntax is deprecated and "expect" syntax is required instead. You can see how I changed your code above to use "expect" syntax. Here's a good blog post on the new syntax (and why the old syntax is deprecated).
it sets up an actual example. An example can be thought of as an actual test which contains one or more expectations (best practice says one expectation per example).
The expect method and matchers only exist in the it block. Variables set up with let and subject are unique for each example.
scenario is an alias for it used in feature (acceptance) specs.
describe, context and feature are used to group examples together. This provides both readability and encapsulation for let and subject variables.
Passing a class to describe also sets up an implicit subject:
RSpec.describe Array do
describe "when first created" do
it { is_expected.to be_empty }
end
end
RSpec has relativly recently undergone a large shift towards eliminating "monkey patching" core ruby classes which depreciated the should syntax.
While it is recommended to use expect for new projects, you can allow should and other monkey patching methods.

Can variables be passed after a do/end block?

I am working with a custom testing framework and we are trying to expand some of the assert functionality to include a custom error message if the assert fails. The current assert is called like this:
assert_compare(first_term, :neq, second_term) do
puts 'foobar'
end
and we want something with the functionality of:
assert_compare(first_term, :neq, second_term, error_message) do
puts 'foobar'
end
so that if the block fails the error message will describe the failure. I think this is ugly, however, as the framework we are moving away from did this and i have to go through a lot of statements that look like:
assert.compare(variable_foo['ARRAY1'][2], variable_bar['ARRAY2'][2], 'This assert failed because someone did something unintelligent when writing the test. Probably me, since in am the one writing this really really long error statement on the same line so that you have to spend a quarter of your day scrolling to the side just to read it')
This type of method call makes it difficult to read, even when using a variable for the error message. I feel like a better way should be possible.
assert_compare(first_term, :neq, second_term) do
puts 'foobar'
end on_fail: 'This is a nice error message'
This, to me, is the best way to do it but i don't know how or if it is even possible to accomplish this in ruby.
The goal here is to make it as aesthetic as possible. Any suggestions?
You could make on_fail a method of whatever assert_compare returns and write
assert_compare(first_term, :neq, second_term) do
puts 'foobar'
end.on_fail: 'This is a nice error message'
In short, no. Methods in ruby take a block as the final parameter only. As Chuck mentioned you could attempt to make the on_fail method a method of whatever assert_compare returns and that is a good solution. The solution I've come up with is not what you are looking for, but it works:
def test block, param
block.call
puts param
end
test proc { puts "hello"}, "hi"
will result in
"hello"
"hi"
What I've done here is create a Proc (which is essentially a block) and then passed it as a regular parameter.

override namespaced puts only works after overriding Kernel.puts?

Sorry for the vague question title, but I have no clue what causes the following:
module Capistrano
class Configuration
def puts string
::Kernel.puts 'test'
end
end
end
Now when Capistrano calls puts, I don't see "test", but I see the original output.
However, when I also add this:
module Kernel
def puts string
::Kernel.puts 'what gives?'
end
end
Now, suddenly, puts actually returns "test", not "what gives?", not the original content, but "test".
Is there a reasonable explanation why this is happening (besides my limited understanding of the inner-workings of Ruby Kernel)?
Things that look off to me (but somehow "seem to work"):
I would expect the first block to return 'test', but it didn't
I would expect the combination of the two blocks to return 'what gives?', but it returns 'test'?
The way I override the Kernel.puts seems like a never-ending loop to me?
module Capistrano
class Configuration
def puts string
::Kernel.puts 'test'
end
def an_thing
puts "foo"
end
end
end
Capistrano::Configuration.new.an_thing
gives the output:
test
The second version also gives the same output. The reason is that you're defining an instance level method rather than a class level method (this post seems to do a good job explaining the differences). A slightly different version:
module Kernel
def self.puts string
::Kernel.puts 'what gives?'
end
end
does the following. Because it is causing infinite recursion, like you expected.
/tmp/foo.rb:14:in `puts': stack level too deep (SystemStackError)
from /tmp/foo.rb:14:in `puts'
from /tmp/foo.rb:4:in `puts'
from /tmp/foo.rb:7:in `an_thing'
from /tmp/foo.rb:18
shell returned 1
I use an answer rather than a comment because of its editing capabilities. You can edit it to add more information and I may delete it later.
Now when Capistrano calls puts, I don't see "test", but I see the
original output.
It's difficult to answer your question without seeing how Capistrano calls puts and which one. I would say it's normal if puts displays its parameter, using the original Kernel#puts (it is not clear what you call original output, I must suppose you mean the string given to puts).
I would expect the first block to return 'test', but it didn't
The only way I see to call the instance method puts defined in the class Configuration in the module Capistrano is :
Capistrano::Configuration.new.puts 'xxx'
or
my_inst_var = Capistrano::Configuration.new
and somewhere else
my_inst_var.puts 'xxx'
and of course it prints test. Again, without seeing the puts statement whose result surprises you, it's impossible to tell what's going on.
I would expect the combination of the two blocks to return 'what gives?', but it returns 'test'?
The second point is mysterious and I need to see the code calling puts, as well as the console output.

Resources