I have a question concerning how to set up multiple tests for the same resource and changing parameters for different tests. The example is minimized to reflect just the problem.
TheGood: I got the first unit test running, where the parameter is a direct parameter of the resource:
mymod/manifests/as/myressource3.pp:
define mymod::as::myressource3 (
Optional[String] $maxheap = undef,
) {
$effective_maxheap = ($maxheap =~ NotUndef) ? {
true => $maxheap,
false => "${::facts['memory']['system']['total_bytes'] * 3 / 4 / 1024 / 1024}M",
}
notice("effective_maxheap = ${effective_maxheap}")
mymod::as::myressource2 {"myres3_myres2-${title}":
maxheap => $effective_maxheap,
}
}
mymod/spec/defines/as/myressource3_spec.rb:
require 'spec_helper'
describe 'mymod::as::myressource3' do
let(:title) { 'as_myressource3_test' }
on_supported_os.each do |os, os_facts|
context "no maxheap on #{os}" do
let(:facts) { os_facts }
let(:params) { {
} }
it { is_expected.to compile }
it { is_expected.to contain_mymod__as__myressource2("myres3_myres2-as_myressource3_test").with({
:maxheap => /^[0-9]+M$/,
})}
context "with maxheap on #{os}" do
let(:params) do
super().merge('maxheap' => '3G')
end
it { is_expected.to compile }
it { is_expected.to contain_mymod__as__myressource2("myres3_myres2-as_myressource3_test").with({
:maxheap => '3G',
})}
end
end
end
end
As said, this works fine and I can change the value of the parameter maxheap for the second test.
TheBad: But in a different situation another resource is using a „global“ variable from an outer class. Using the same approach as in „TheGood“, I am not able the change the parameter in the second context.
mymod/manifests/as/myressource.pp:
define mymod::as::myressource (
) {
$effective_maxheap = ($::mymod::maxheap =~ NotUndef) ? {
true => $::mymod::maxheap,
false => "${::facts['memory']['system']['total_bytes'] * 3 / 4 / 1024 / 1024}M",
}
notice("effective_maxheap = ${effective_maxheap}")
mymod::as::myressource2 {"myres_myres2-${title}":
maxheap => $effective_maxheap,
}
}
notice the use of $::mymod::maxheap!
mymod/manifests/init.pp:
class mymod(
Optional[String] $maxheap = undef,
) {
notice("${title} start maxheap=${maxheap} ...")
mymod::as::myressource {'whatever': }
notice("${title} end ...")
}
mymod/spec/defines/as/myressource_spec.rb:
require 'spec_helper'
describe 'mymod::as::myressource' do
let(:title) { 'as_myressource_test' }
on_supported_os.each do |os, os_facts|
context "no maxheap on #{os}" do
let(:facts) { os_facts }
let(:pre_condition) do
"
class { 'mymod':
}
"
end
it { is_expected.to compile }
it { is_expected.to contain_mymod__as__myressource2("myres_myres2-as_myressource_test").with({
:maxheap => /^[0-9]+M$/,
})}
context "with maxheap on #{os}" do
let(:params) do
super().merge('::mymod::maxheap' => '3G')
end
it { is_expected.to compile }
it { is_expected.to contain_mymod__as__myressource2("myres_myres2-as_myressource_test").with({
:maxheap => '3G'
})}
end
end
end
end
This is giving the following exeption:
1) mymod::as::myressource no maxheap on sles-12-x86_64 with maxheap on sles-12-x86_64 is expected to compile into a catalogue without dependency cycles
Failure/Error: super().merge('::mymod::maxheap' => '3G')
NoMethodError:
super: no superclass method `params' for #<RSpec::ExampleGroups::MymodAsMyressource::NoMaxheapOnSles12X8664::WithMaxheapOnSles12X8664:0x000000000781c5c0>
Did you mean? param_str
# /opt/puppetlabs/pdk/share/cache/ruby/2.5.0/gems/rspec-puppet-2.7.8/lib/rspec-puppet/matchers/dynamic_matchers.rb:7:in `method_missing'
# ./spec/defines/as/myressource_spec.rb:23:in `block (5 levels) in <top (required)>'
# /opt/puppetlabs/pdk/share/cache/ruby/2.5.0/gems/rspec-puppet-2.7.8/lib/rspec-puppet/support.rb:149:in `test_manifest'
# /opt/puppetlabs/pdk/share/cache/ruby/2.5.0/gems/rspec-puppet-2.7.8/lib/rspec-puppet/support.rb:21:in `build_code'
# /opt/puppetlabs/pdk/share/cache/ruby/2.5.0/gems/rspec-puppet-2.7.8/lib/rspec-puppet/support.rb:91:in `block in load_catalogue'
# /opt/puppetlabs/pdk/share/cache/ruby/2.5.0/gems/rspec-puppet-2.7.8/lib/rspec-puppet/support.rb:376:in `with_vardir'
# /opt/puppetlabs/pdk/share/cache/ruby/2.5.0/gems/rspec-puppet-2.7.8/lib/rspec-puppet/support.rb:83:in `load_catalogue'
# /opt/puppetlabs/pdk/share/cache/ruby/2.5.0/gems/rspec-puppet-2.7.8/lib/rspec-puppet/example/define_example_group.rb:7:in `catalogue'
# /opt/puppetlabs/pdk/share/cache/ruby/2.5.0/gems/rspec-puppet-2.7.8/lib/rspec-puppet/support.rb:12:in `block in subject'
# /opt/puppetlabs/pdk/share/cache/ruby/2.5.0/gems/rspec-puppet-2.7.8/lib/rspec-puppet/matchers/compile.rb:23:in `matches?'
# ./spec/defines/as/myressource_spec.rb:25:in `block (5 levels) in <top (required)>'
How can I change ::mymod::maxheap for the second test? Or maybe this example is no good puppet style?
I think there are two ways to fix this. first in you have the following
define mymod::as::myressource (
) {
$effective_maxheap = ($::mymod::maxheap =~ NotUndef) ? {}
}
As you make use of the mymod class i would explicitly include it e.g.
define mymod::as::myressource (
) {
include mymod
$effective_maxheap = ($::mymod::maxheap =~ NotUndef) ? {}
}
This means that when your rspec tests run mymod will automaticity be included, all code in it compiled, and you wont need to mock anything in rspec.
If this is not possible or desirable then you can use pre_condition to initiate the class for you. Again this will cause everything inside mymod to also get compiled when running the spec test
describe 'mymod::as::myressource' do
let(:title) { 'as_myressource_test' }
on_supported_os.each do |os, os_facts|
context "no maxheap on #{os}" do
let(:facts) { os_facts }
let(:pre_condition) { "class {'mymod': $maxheap = undef" }
end
end
end
if compiling the code in mymod is undesirable and you really just want to mock the maxheap variable you can do the following
describe 'mymod::as::myressource' do
let(:title) { 'as_myressource_test' }
on_supported_os.each do |os, os_facts|
context "no maxheap on #{os}" do
let(:facts) { os_facts }
let(:pre_condition) { "class mymod { $maxheap = undef }" }
end
end
end
Related
I've got a class where in initializer I need to call instance variable from parsed params:
class PrintResults
include SortResults
attr_accessor :views_hash
def initialize(parser)
#parser = parser
#views_hash = parser.page_views
end
I want to test attributes accessors, I tried something below:
RSpec.describe PrintResults do
subject { described_class.new(views_hash) }
describe 'attributes accessors' do
let(:accessors) { double(page_views: { '/that_70s_show' => ['111.111.111.111'] }) }
it 'should have views hash' do
subject.views_hash = accessors
expect(subject.views_hash).to eq(['111.111.111.111'])
end
end
but I'm getting an error:
1) PrintResults attributes accessors should have views hash
Failure/Error: expect(subject.views_hash).to eq(['111.111.111.111'])
expected: ["111.111.111.111"]
got: #<Double (anonymous)>
(compared using ==)
Diff:
## -1 +1 ##
-["111.111.111.111"]
+#<Double (anonymous)>
You assign your test double directly to the attribute that is returned instead of using the initialize method.
Instead of
subject { described_class.new(views_hash) }
describe 'attributes accessors' do
let(:accessors) { double(page_views: { '/that_70s_show' => ['111.111.111.111'] }) }
it 'should have views hash' do
subject.views_hash = accessors
expect(subject.views_hash).to eq(['111.111.111.111'])
end
end
use
subject { described_class.new(parser) }
describe 'attributes accessors' do
let(:parser) { double(page_views: { '/that_70s_show' => ['111.111.111.111'] }) }
it 'should have views hash' do
expect(subject.views_hash).to eq('/that_70s_show' => ['111.111.111.111'])
end
end
I want to implement rspec with expect. I tried this:
RSpec:
describe WechatRequestBuilder do
let(:request_builder) { described_class.new(env: 'test_env') }
let(:trx_types) { ['wechat'] }
let(:trx_type) { 'wechat' }
let(:gateway) { 'wechat' }
let(:currency) { 'CNY' }
let(:base_params) { request_builder.send(:base_params) }
it_behaves_like 'request builder', true
context '#submit!' do
it "sends test transactions" do
allow(request_builder).to receive(:process_trx).with(trx_types, gateway)
binding.pry
request_builder.submit!
expect(request_builder.submit!).to receive(:process_trx).with(trx_types, gateway)
end
end
end
Request modifier:
class RequestModifier
def get_trx_type(request_body)
doc = Nokogiri::XML(request_body)
doc.search("transaction_type").first.text
end
end
I tried to find some object with binding.pry but without a luck:
[1] pry(#<RSpec::ExampleGroups::WechatRequestBuilder::Submit>)> request_builder
=> #<WechatRequestBuilder:0x007ffc1af4fd80 #env="test_env", #request_modifier=#<RequestModifier:0x007ffc1af4fd30>>
Can you give e some example based on the above code what should I configure as 'expect'? Currently I get:
(nil).process_trx(["wechat"], "wechat")
expected: 1 time with arguments: (["wechat"], "wechat")
received: 0 times
I have this rspec task which I would like to implement with stubbed config file:
let(:request_builder) { described_class.new(env: nil) }
let(:trx_types) { ['davivienda'] }
let(:trx_type) { 'davivienda' }
let(:gateway) { 'girogate' }
let(:currency) { 'USD' }
let(:base_params) { request_builder.send(:base_params) }
before(:each) { allow(request_builder).to receive(:currency).and_return('USD') }
let(:yaml_file) { YAML::load(File.read(File.join('spec', 'fixtures', 'yaml', 'env.yml'))) }
let(:config) { yaml_file['SOF_DEV'] }
context '#submit!' do
it "sends test transactions" do
allow(request_builder).to receive(config).and_return(config)
request_builder.submit!
PAYMENT_TYPE_WITH_BASE_PARAMS.each do |x|
expect(request_builder).te receive(:process_trx).with(factory(x), :gateway, :base_params)
end
end
end
I get error at this line:
allow(request_builder).to receive(config).and_return(config)
NoMethodError:
undefined method `to_sym' for #<Hash:0x007f86484eb440>
Do you know how I can fix this issue?
You've passed in config rather than :config to the expected call.
It should be:
allow(request_builder)
.to receive(:config)
.and_return(config)
I have problem in my previous question, me helped, but and now I've took new.
I'm make integration tests with rspec and capybara.
this my profiles_controllers.rb :
before_filter :authenticate_user!
def update
#profile = current_user.profile
if #profile.update_attributes(params[:profile])
flash[:success] = "Профиль обновлен!"
redirect_to user_path(current_user)
else
render 'edit'
end
end
it's my test file:
describe "ProfilePages" do
subject { page }
describe "edit" do
let(:user) { FactoryGirl.create(:user) }
let(:profile) { FactoryGirl.create(:profile, user: user) }
before do
login user
visit edit_profile_path(profile)
end
it { should have_selector('h2', text: 'Заполните информацию о себе') }
describe "change information" do
let(:new_city) { "Ulan-Bator" }
let(:new_phone) { 1232442 }
let(:new_gamelevel) { "M2" }
let(:new_aboutme) { "nfsfsdfds" }
let(:submit) { "Сохранить" }
before do
fill_in "Город", with: new_city
fill_in "Телефон", with: new_phone
select new_gamelevel, from: "Уровень игры"
fill_in "О себе", with: new_aboutme
click_button submit
end
specify { profile.reload.city.should == new_city }
specify { profile.reload.phone.should == new_phone }
specify { profile.reload.gamelevel.should == new_gamelevel }
specify { profile.reload.aboutme.should == new_aboutme }
end
describe "submitting to the update action" do
before { put profile_path(profile) }
specify { response.should redirect_to(user_path(user)) }
end
end
end
And I have error:
Failure/Error: specify { response.should redirect_to(user_path(user)) }
Expected response to be a redirect to http://www.example.com/users/1 but was a redirect to http://www.example.com/users/sign_in
I use Devise and have login helper in spec/support:
def login(user)
page.driver.post user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
end
And config.include Devise::TestHelpers, :type => :controller in spec_helper.rb
I tried use warden helper login_as , but have same error. How I understand it's don't start session, I'am right?
This is nothing to do with your app code, but the test code.
response object is for controller integration tests, and there is no such object in Capybara.
Normally you can use page object to check response information. And for path checking, a better approach is current_path or current_url.
So your code will work by:
current_path.should be(user_path(user))
This is one of those cases where my code is working but my test is failing and I need to know what I am doing wrong?
I have a Project class with an all method that just spits out instances of this class:
class Project
##all_projects = []
def initialize(options)
##all_projects << self
end
def self.all
##all_projects
end
end
Now Project.all works just fine but the spec I am writing doesn't.
context "manipulating projects" do
before do
options1 = {
name: 'Building house'
}
options2 = {
name: 'Getting a loan from the Bank'
}
#project1 = Project.new(options1)
#project2 = Project.new(options2)
end
it "can print all projects" do
Project.all.should eq([#project1, #project2])
end
The failure message I get is:
Project manipulating projects can print all projects
Failure/Error: Project.all.should eq([#project1, #project2])
expected: [Building house, Getting a loan from the Bank]
got: [Building house, Building house, Building house, Getting a loan from the Bank, Building house, Getting a loan from the Bank]
Here is the full spec in a gist: https://gist.github.com/4535863
What am I doing wrong? How can I fix it?
It is doubling the results because it runs the before block for each test, where the class attribute is modified (when two new projects are initialized), and (according to the gist) the test you're referring to is the second one.
To avoid the problem you'll need to reset ##all_projects in an after block:
after do
Project.class_variable_set :##all_projects, []
end
See also: How can I clear class variables between rspec tests in ruby
(Thanks to #iain for the suggestion to move the reset code to an after block rather than a before block.)
This doesn't use before blocks to set stinky instance variables.
describe Project do
let(:options1){
{
name: 'Building house',
priority: 2,
tasks: []
}
}
let(:options2) {
{
name: 'Getting a loan from the Bank',
priority: 3,
tasks: []
}
}
let(:project1) { Project.new(options1) }
let(:project2) { Project.new(options2) }
context "while starting up" do
subject { Project.new options1 }
its(:name) { should include('Building house') }
its(:tasks) { should be_empty }
end
context "manipulating projects" do
before :all do
Project.all.clear
end
subject { Project.all }
its(:count) { should be > 0 }
it { should eq [project1, project2] }
end
end