I'm writing a test fot rspec to check that items are added into a class that I use like an array
describe '#collection' do
let(:process) {
instance_double("WebServerProcess", :cpu => 33, :mem => 22, :pid => 1, :port => 8000)
}
it 'return the collection' do
WebServersCollection.add process
expect(subject.collection).to eq([process])
end
it 'should add with <<' do
WebServersCollection << process
expect(subject.collection).to eq([process])
end
end
Show me this error
Failure/Error: expect(subject.collection).to eq([process])
# was originally created in one example but has leaked into another example and can no
longer be used. rspec-mocks' doubles are designed to only last for one
example, and you need to create a new one in each example you wish to
use it for.
I think the error tells you everything you need to know.
You can't do that and
you need to create a new one in each example you wish to use it for
Related
I want a number to be printed like "42/100". This formatting should be done by a function which I am gonna use in two different models. where should I add that method?
Okay, as per the limited description, here is what i propose:
formatting_helper.rb
module FormattingHelper
MAXIMUM_VALUE = 100.freeze # Change it here.
def self.divide_by_maximum_value(str)
"#{str}/#{MAXIMUM_VALUE}"
end
end
And then you will be able to call FormattingHelper.divide_by_maximum_value from anywhere. e.g.
FormattingHelper.divide_by_maximum_value('42')
=> "42/100"
If you want to create a number to percentage you can use
number helper from ruby on rails official guide
number_to_percentage(42)
# => 42.000%
except those guide, you should create module like jagdeep singth said.
or create lambda
num_formatter = ->(x,y='100') { x.to_s+"/#{y.to_s}" }
num_formatter.call(42)
# => "42/100"
num_formatter.call(42,1000)
# => "42/1000"
The real issue I am encountering is the delay on population of drop down lists in a web page.
The call i am using in the script is :-
clickOndropDown(":id","dropDownAutocomplete","ABC",#b)
where
1. clickOndropDown is the method invoked
2. id is the element selector (which can be :id,xpath, or :css [which are different selectors in my html code for different dropdown boxes] to handle all cases within the code)
3. ABC is the element to be selected from the drop down list
4. #b is the browser object
the function definition is as :
def clickOndropDown(identifier,selector,targetVal,br)
begin
puts "starting off" # gets printed
br.element("#{identifier}" => "#{selector}").fire_event :click #--- does not work---
# br.element(:id => "#{selector}").fire_event :click #--- works
puts "inside selector pass 1"
br.element(:link, targetVal).fire_event :click
...
...
# rest of the code
It does not throw any exception..just does the push statement before the selection statement.
is there any wild card handler for # (--"#{identifier}" ) so that I can write a single code for handling id,xpath,or css
I am surprised that there is no exception. I would expect a MissingWayOfFindingObjectException.
The problem is that the identifier is a String instead of a Symbol. The line that is not working is equivalent to:
br.element(":id" => "dropDownAutocomplete")
Which is not the same as:
br.element(:id => "dropDownAutocomplete")
Notice the difference between the String, ":id", and the Symbol, :id.
You should change the method call to send a Symbol and change the method to no longer change it to a String:
def clickOndropDown(identifier,selector,targetVal,br)
br.element(identifier => selector).fire_event :click
end
clickOndropDown(:id,"dropDownAutocomplete","ABC",#b)
If you want to have a more versatile method, you would be better off to have the clickOndropDown accept a Hash for the locator. This would allow you to accept multiple locator criteria.
def click_on_dropdown(br, target_val, locator)
br.element(locator).fire_event :click
end
# Call for the original example
click_on_dropdown(#b, "ABC", id: "dropDownAutocomplete")
# Call with multiple selectors
click_on_dropdown(#b, "ABC", id: "dropDownAutocomplete", name: "name_value")
I have a Chef recipe for a multi-node web service, each node of which needs to get the hostname and IP of the other nodes, to put it into its own local configuration.
The code is shown below. The problem is that when the node.set[][] assignments are made in the ruby_block as shown, the values are empty when the template that relies upon them is created. If I want to create that template, I have to move all of the ruby_block code outside, and have it "loose" in the recipe. Which makes it harder to do unit-testing with Chefspec and the like.
Can any Chef guru set me straight? Is it just impossible to do node.set[] like this inside of a ruby_block? And if so, why doesn't it say so in the docs?
$cm = { :name => "web", :hostname => "" , :ip_addr => "" }
$ca = { :name => "data", :hostname => "" , :ip_addr => "" }
$cg = { :name => "gateway", :hostname => "" , :ip_addr => "" }
$component_list = [$cm, $ca, $cg]
ruby_block "get host addresses" do
block do
for cmpnt in $component_list
# do REST calls to external service to get cmpnt.hostname, ip_addr
# .......
node.set[cmpnt.name]['name'] = cmpnt.name
node.set[cmpnt.name]['host'] = cmpnt.hostname
node.set[cmpnt.name]['ip'] = cmpnt.ip_addr
end
end
end
template "/etc/app/configuration/config.xml" do
source "config.xml.erb"
variables( :dataHost => node['data']['host'],
:webHost => node['web']['host'],
:gatewayHost => node['gateway']['host'] )
action :create
end
I also added
subscribes :create, "ruby_block[get host addresses]", :immediately
to the template definition to ensure that the ruby_block ran before the template was created. This didn't make a difference.
I realize this is an old post, however for future reference, I just ran across this gist which gives a nice example of node variable assignments in the Compile vs. Converge phases. To adapt the gist to your example, you'll need to add code like the following to your ruby_block:
template_r = run_context.resource_collection.find(:template => "/etc/app/configuration/config.xml")
template_r.content node['data']['host']
template_r.content node['web']['host']
template_r.content node['gateway']['host']
For Chef 11, also see Lazy Attribute Evaluation.
The problem seems to be that attribute values inside your template resource definition get evaluated before actually invoking any resources.
I.e. the file is first executed as simple Ruby, compiling the resources, and only the the resource actions gets invoked. By that time, it is too late already.
I ran into the same problem when trying to encapsulate certain attribute manipulations into a resource. It simply does not work. Should anyone know a solution to this problem, I would appreciate it very much.
EDIT:
b = ruby_block...
...
end
b.run_action(:create)
Could possibly do the trick. It invokes the resource immediately.
The simplest answer to this is to not use chef attributes and not use ruby_block to do the work of talking to the REST API. The code can also be moved to a custom resource for better reuse:
unified_mode true
provides :my_resource
action :run do
cm = { :name => "web", :hostname => "" , :ip_addr => "" }
ca = { :name => "data", :hostname => "" , :ip_addr => "" }
cg = { :name => "gateway", :hostname => "" , :ip_addr => "" }
component_list = [cm, ca, cg]
hash = {}
for cmpnt in component_list
# do REST calls to external service to get cmpnt.hostname, ip_addr
# .......
hash[cmpnt.name] = {}
hash[cmpnt.name]['name'] = cmpnt.name
hash[cmpnt.name]['host'] = cmpnt.hostname
hash[cmpnt.name]['ip'] = cmpnt.ip_addr
end
template "/etc/app/configuration/config.xml" do
source "config.xml.erb"
variables( :dataHost => hash['data']['host'],
:webHost => hash['web']['host'],
:gatewayHost => hash['gateway']['host'] )
action :create
end
end
By using unified_mode and moving into a custom resource, it also makes it easier to use a node attribute without requiring the use of lazy {} or ruby_blocks. It also still allows chef configuration (like setting up resolv.conf or other network requirements before doing the REST calls) prior to calling this code while not having to think about compile/converge two pass issues in recipe context.
There is also no reason to use a resource like ruby_block to do pure ruby processing which does not change the system under management. In this case the ruby_block is hitting a REST service purely to collect data. That does not need to be placed into a Chef resource. It isn't clear from the question if that was being done because the questioner though it was a "best practice" (in this case it is not), or if it was being done to move execution to compile time in order to allow other chef resources that aren't part of the question to fire first (in which case using a custom resource is a much better solution than using a ruby_block).
It's been a while since this question, but in case someone is still looking for it, lazy evaluate is your friend:
template '/tmp/sql_file.sql' do
source "sql_file.sql.erb"
mode 0700
variables lazy {
# Create a new instance of MySQL library
mysql_lib = Acx::MySQL.new(
'127.0.0.1', 'root', node['mysql']['service']['pass']
)
password = node['mysql']['service']['support_admin']['ct_password']
# It returns the encrypted password after evaluate it, to
# be used in template variables
{ admin_password: mysql_lib.encrypted_password(password) }
}
end
https://docs.chef.io/resource_common.html#lazy-evaluation
I am testing a static class method, passing many different inputs into it. Since each input is quite big, I saved the list somewhere and pass each to the method in a loop.
However I realize that the errors does not display the input to these methods, making it difficult to debug. Is there a way to display this instead?
Perhaps you could label the inputs (e.g. in an hash) and use the labels to create the example titles, so that you know which input produced the unexpected results?
E.g.
INPUTS = {
"returns an error when input is too big" => { :input => ..., :output => ... },
"returns an error when input too small" => { :input => ..., :output => ... },
"returns a pony when input is just right" => { :input => ..., :output => ... },
}
INPUTS.each do |title, bundle|
it title do
StaticClass.method(bundle[:input]).should == bundle[:output]
end
end
This example is silly; obviously, you'd want to load your data out of yaml files or whatever, but I was too lazy to pop demo code for that. Dir.glob.somethingorother. :-)
Also, note that the problem you're trying to solve is an inherent problem in fixture-driven tests; you have to look somewhere outside the example code to see what the example code was doing. If you can avoid fixture-driven tests, I recommend that you do so.
n the .net world, my specs would follow the Arrange, Act, Assert pattern. I'm having trouble replicating that in rspec, because there doesn't appear to be an ability to selectively verify your mocks after the SUT has taken it's action. That, coupled with the fact that EVERY expectation is evaluated at the end of each 'It' block, is causing me to repeat myself in a lot of my specs.
Here's an example of what I'm talking about:
describe 'AmazonImporter' do
before(:each) do
Kernel.**stubs**(:sleep).with(1)
end
# iterates through the amazon categories, and for each one, loads ideas with
# the right response group, upserting ideas as it goes
# then goes through, and fleshes out all of the ideas that only have asins.
describe "find_new_ideas" do
before(:all) do
#xml = File.open(File.expand_path('../amazon_ideas_in_category.xml', __FILE__), 'r') {|f| f.read }
end
before(:each) do
#category = AmazonCategory.new(:name => "name", :amazon_id => 1036682)
#response = Amazon::Ecs::Response.new(#xml)
#response_group = "MostGifted"
#asin = 'B002EL2WQI'
#request_hash = {:operation => "BrowseNodeLookup", :browse_node_id => #category.amazon_id,
:response_group => #response_group}
Amazon::Ecs.**expects**(:send_request).with(has_entries(#request_hash)).returns(#response)
GiftIdea.expects(:first).with(has_entries({:site_key => #asin})).returns(nil)
GiftIdea.any_instance.expects(:save)
end
it "sleeps for 1 second after each amazon request" do
Kernel.**expects**(:sleep).with(1)
AmazonImporter.new.find_new_ideas(#category, #response_group)
end
it "loads the ideas for the given response group from amazon" do
Amazon::Ecs.**expects**(:send_request).
with(has_entries(#request_hash)).
returns(#response)
**AmazonImporter.new.find_new_ideas(#category, #response_group)**
end
it "tries to load those ideas from repository" do
GiftIdea.expects(:first).with(has_entries({:site_key => #asin}))
**AmazonImporter.new.find_new_ideas(#category, #response_group)**
end
In this partial example, I'm testing the find_new_ideas method. But I have to call it for each spec (the full spec has 9 assertion blocks). I further have to duplicate the mock setup so that it's stubbed in the before block, but individually expected in the it/assertion block. I'm duplicating or nearly duplicating a ton of code here. I think it's even worse than the highlighting indicates, because a lot of those globals are only defined separately so that they can be consumed by an 'expects' test later on. Is there a better way I'm not seeing yet?
(SUT = System Under Test. Not sure if that's what everyone calls it, or just alt.net folks)
You can use shared example groups to reduce duplication:
shared_examples_for "any pizza" do
it "tastes really good" do
#pizza.should taste_really_good
end
it "is available by the slice" do
#pizza.should be_available_by_the_slice
end
end
describe "New York style thin crust pizza" do
before(:each) do
#pizza = Pizza.new(:region => 'New York' , :style => 'thin crust' )
end
it_behaves_like "any pizza"
it "has a really great sauce" do
#pizza.should have_a_really_great_sauce
end
end
Another technique is to use macros, which is handy if you need similar specs in different classes.
Note: the example above is borrowed from The RSpec Book, Chapter 12.
you can separate them using "context" if that helps...
https://github.com/dchelimsky/rspec/wiki/faq