How display failure on undescribed example in RSpec? - ruby

I am describing class on RSpec
class Pupil
def initialize(name, dateOfBirth)
#name = name
#dateOfBirth = dateOfBirth
end
def name
#name
end
def ages
#should calculate ages
end
end
describe Pupil do
context do
pupil = Pupil.new("Stanislav Majewski", "1 april 1999")
it "should returns name" do
pupil.name.should eq("Stanislav Majewski")
end
it "should calculates ages" do
#not described
end
end
end
RSpec returns:
..
Finished in 0.00203 seconds
2 examples, 0 failures
Is there an elegant way to display a failure message that the method is not described?

If you're concerned that you'll create a test and forget to put anything it in (sometimes I'll create three tests I know I'll need, and work on each of them in turn) then you can do the following:
it "should calculates ages" do
fail
end
OR
it "should calculates ages"
...and that's all (no block) will mark the test as pending automatically. In other words, don't fill out your tests until they have actual test code in them.
Also, if you don't test any assertions (i.e. if your spec doesn't contain any lines that have a call to should in them), your spec will appear to pass. This has happened to me a few times, where I write a new test, expecting it to fail, and it doesn't because I forgot to include the call to should which is what actually tests the assertion.

Related

Making a method return a specific value (that would otherwise be random) in Rspec

I'm trying to write an rspec test that would assume a method will return a specific value when it would normally return a random number between 1 and 10.
Here is the code in my lib directory:
def take_off(plane)
fail "Bad weather today. Cannot take off." if stormy_weather? > 8
plane.take_off_plane
planes.delete(plane)
end
def stormy_weather?
rand(10)
end
And here is my test in Rspec:
it 'raises an error when weather is stormy' do
plane = double(:plane)
stormy_weather = 9
allow(plane).to receive(:take_off_plane)
expect{ subject.take_off(plane) }.to raise_error("Bad weather today. Cannot take off.")
end
Thank you in advance to anyone who helps!
You can specify the returned value with and_return:
allow(subject).to receive(:stormy_weather?).and_return(9)

Yielding self to rspec test inside each loop yields last known state

I'm writing some rspec tests for some web-pages. One of the pages contains several links that I want to test as a group. So my test looks something like this
require 'spec_helper'
t = Page.new
t.test do |t|
describe 'a thing' do
it 'should not be last' do
t.title
end
end
end
So when I call t.title I am actually calling that on the the following Page object being yielded (by itself) down below.
and my Page object looks like this
class Page
attr_accessor :driver
def initialize()
#driver = Watir::Browser.new :phantomjs
#home = ''
#driver.goto(#home)
end
def visit(url)
#driver.goto(url)
end
def title
#driver.title
end
def test
#subpages.each do |page|
visit(page)
yield self
end
end
end
So now when I run rspec, what ends up happening is the test will run as many times as I expect it to, however it runs each time it yields the object in the state it's in during the final iteration of visit. So it's not really testing the pages the way I want it to, it's testing the last page in the list.
Am I incorrectly using yield or self here? It seems pretty straightforward: pass the test as a block to the Page object's test method and have it run the test on itself.
Any tips? I'd like to be able to keep all the tests clean, and all the logic in the page object, but this is hindering me from doing so.
Within the scope of a given file, RSpec examples/tests don't get executed until they all have been defined. You're iterating through the page defining all these examples, but RSpec is collecting and not executing them until the iteration is complete, at which time the value of t remains unchanged and corresponds to the final state of the page.

rspec way for passing variable between multiple contexts

I was wondering what would be the best way to pass variable between multiple contexts (or multiple its) in rspec but without using global variables?
For example, I have this:
describe "My test" do
let(:myvar) { #myvar = 0 }
context "First test pass" do
it "passes" do
myvar = 20
expect(myvar).to eq(20)
end
end
context "Second test pass" do
it "passes" do
expect(myvar).to eq(20)
end
end
end
Now, obviously, this will not work with let because with new context, myvar variable will be back on initial state which is = 0.
I would need mechanism to "cache state" between two contexts which would in turn give me value of myvar = 20 in second context
Any opinions, suggestions and improvements are welcome.
Thanks
Another simple way, would be to define a 'local variable' in describe context.
the 'local variable' would live throughout the describe, and any changes during run time would effect it, and so change it.
For example
describe 'tests' do
context 'Sharing a variable across tests' do
var = 1
puts var
it "it one. var = #{var}" do
var = var*2
puts var
end
it "it two" do
puts var
end
end
end
Output
1
2
1
What happens is not what you think happens.
What you want to happen break "unit testing" as a methodology.
Let me explain #2 first - unit testing test cases should be able to work in isolation, which means that they should work when run together, when run apart, and in any order... so much so that some unit testing frameworks (like the default one in elixir) run test cases in parallel...
As for #1 - when you write myvar = 20 you are not assigning a value to let(:myvar) { #myvar = 0 }, you simply create a local variable, which will override all calls to myvar within the method, but will not be available outside the method (myvar will return 0).
Even if you would have set #myvar = 20 (unless you do it before you call myvar for the first time) instead, myvar would still return 0, since the let function is using a memento pattern, which means it is called once, and subsequent calls return the value originally returned (in this case 0):
puts myvar
#myvar = 20
puts myvar
# => 0
# => 0
I just ran into this same problem. How I solved it was by using factory_girl gem.
Here's the basics:
create a factory:
require 'factory_girl'
require 'faker' # you can use faker, if you want to use the factory to generate fake data
FactoryGirl.define do
factory :generate_data, class: MyModule::MyClass do
key 100 # doesn't matter what you put here, it's just a placeholder for now
another_key 'value pair'
end
end
Now after you made the factory you need to make a Model that looks like this:
Module MyModule
class MyClass
#for every key you create in your factory you must have a corresponding attribute accessor in the model.
attr_accessor :key, :another_key
#you can also place methods here to call from your spec test, if you wish
# def self.test
#some test
# end
end
end
Now going back to your example you can do something like this:
describe "My test" do
let(:myvar) { #myvar }
context "First test pass" do
it "passes" do
#myvar.key = 20 #when you do this you set it now from 100 to 20
expect(#myvar.key).to eq(20)
end
end
context "Second test pass" do
it "passes" do
expect(#myvar.key).to eq(20) #it should still be 20 unless you overwrite that variable
end
end
end
As stated by others, not proper way of unit testing. But, how should we know if you're unit testing or not. So, I won't judge.
Anyways, good luck let us know, if you got some other solution!

Testing with Rspec - The correct way

My weakest point when it comes to coding, is using TDD & BDD methods - I tend to just write code.. but it is something that I am trying to work on.
Could anyone point out the best way to go about the following problem:
Class1:
module TempMod
class MyClass
def initalize(config)
#config = config
end
def process(xml)
if react_upon? xml.something
puts 'yeah'
else
puts 'nah'
end
end
def react_upon?(xml_code)
#code here
end
end
end
So lets say I wanted to test this class, or build it from a TDD point of view so I write my tests:
describe TempMod::MyClass do
let(:config) {double}
let(:myclass) {TempMod::MyClass.new config}
context 'Given that the xml is something we react upon' do
it 'should check that it is valid' do
myclass.process '<some><xml>here</xml></some>'
end
it 'should output yea'
end
end
How do I test that it is calling the react_upon? method. Do I even want to see it is calling it?
Is the proper way to test it, to test all the functions like the react_upon? itself independently of the other functions?
This is properly the main thing that is most confusing me with this sort of testing. Am I testing the whole class, or just individually testing the functions, and not their interactions with the other functions in that class?
Also I realize the the react_upon? might not adhere to the Single responsibility principle and I would probably move that out to its own module/class which I could test using a stub.
If anyone can shed some light on this for me that would be awesome.
edit:
describe TempMod::MyClass do
let (:valid_planning_status_xml) {
'<StatusUpdate> <TitleId>2329</TitleId> <FromStatus>Proposed</FromStatus> <ToStatus>Confirmed</ToStatus> </StatusUpdate>'
}
let(:config) { double }
let(:status_resolver) { double }
subject(:message_processor) { TempMod::MyClass.new config, status_resolver }
context 'Given that the message XML is valid' do
it 'should check the context of the message' do
expect(message_processor.process valid_planning_status_xml).to call :check_me
end
context 'Given that the message is for a planning event update' do
it 'should call something' do
pending
end
end
context 'Given that the message is for a recording job update' do
end
context 'Given that the message is for a video title update' do
end
end
end
Your question confused me a bit is this what you are asking
module TempMod
class MyClass
def initalize(config)
#config = config
end
def process(xml)
react_upon?(xml.something) ? 'yeah' : 'nah'
end
def react_upon?(xml_code)
#code here
end
end
end
Then test like
describe TempMod::MyClass do
let(:config) {double}
let(:myclass) {TempMod::MyClass.new config}
context 'Given that the xml is something we react upon' do
it "should respond to react_upon?" do
expect(myclass).to respond_to(:react_upon?)
end
it "should react_upon? valid xml" do
expect(myclass.react_upon?(YOUR VALID REACTION GOES HERE)).to be_true
end
it "should not react_upon? invalid xml" do
expect(myclass.react_upon?(YOUR INVALID REACTION GOES HERE)).to be_false
end
it "should say 'yeah' if it is valid" do
expect(myclass.process('<some><xml>here</xml></some>')).to eq('yeah')
end
it "should say 'nah' if it is invalid" do
expect(myclass.process('<some><xml>here</some>')).to eq('nah')
end
it 'should check the context of the message' do
expect(myclass).to receive(:react_upon?).with('<some><xml>here</xml></some>')
myclass.process('<some><xml>here</xml></some>')
end
end
end
Right now your tests have no expectations so I added one that expects myclass to respiond_to the react_upon? method and another that expects myclass.process(xml) to respond with a String that equals yeah.

Mocha expected at most once, invoked twice, but method is clearly invoked only once

I'm using Mocha for mock testing. Below is the relevant code:
# test_player.rb
should "not download the ppg more than once for a given year" do
#durant.expects(:fetch_points_per_game).at_most_once
ppg = #durant.points_per_game
ppg2= #durant.points_per_game
assert_equal ppg, ppg2, "A player should have a points per game"
end
# player.rb
class Player
# ...
def points_per_game(year=Date.today.year)
#points_per_game ||= fetch_points_per_game(year)
end
alias_method :ppg, :points_per_game
private
def fetch_points_per_game(year=Date.today.year)
31.2
end
end
The test failed, complaining that there was an "unexpected invocation: #.fetch_points_per_game(any_parameters)"
My understanding of my code is if #point_per_game is nil, fetch_points_per_game will be called, otherwise, the result is cached for future calls to points_per_game. So why is the test complaining that fetch_points_per_game was called twice?
In your expectation, you're not specifying a return value, so the stubbed call returns nil. This is why it's being called a second time. If you change the expectation to:
#durant.expects(:fetch_points_per_game).at_most_once.returns(1.23)
You should find that the tests now pass.

Resources