undefined method `to_sym' when yaml file is stubbed - ruby

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)

Related

Puppet unit test changing parameters between tests

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

RSpec double/mock instance variable from initializer

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

Configure expect in rspec

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

RSpec spies work differently on two different classes

I have a class Uploader which takes a file and uploads it to S3. I'm trying to test that #s3 is actually receiving a file body when upload_file is called. When I test that File is getting messages sent, the test passes. However, trying to spy on Aws::S3::Client does not work.
class Uploader
def initialize(tmp_dir_name, bucket)
#base_tmp_dir = tmp_dir_name
#s3 = Aws::S3::Client.new(region: 'us-east-1')
#bucket = bucket
#uploaded_assets = []
end
def upload_file(key, file_path)
file = File.new(file_path)
#s3.put_object(bucket: #bucket, key: key.to_s, body: file.read)
end
end
RSpec.describe Uploader do
let(:bucket) { 'test_bucket' }
let(:base_temp_dir) { 'test_temp_dir' }
let(:uploader) { Uploader.new(base_temp_dir, bucket) }
describe "#upload_file" do
let(:file) { double('file') }
before { allow(File).to receive(:new) { file } }
before { allow(file).to receive(:read).and_return('text') }
before { allow(Aws::S3::Client).to receive(:put_object) }
it "uses one file" do
uploader.upload_file('test_key', 'file_path')
expect(File).to have_received(:new).with('file_path')
end
it "sends data to s3" do
uploader.upload_file('test_key', 'file_path')
expect(Aws::S3::Client).to have_received(:put_object)
end
end
end
I ended up mocking out s3 for this particular test.
it "sends data to s3" do
test_key = 'test_key'
bucket = 'test_bucket'
fake_s3 = instance_double(Aws::S3::Client)
allow(Aws::S3::Client).to receive(:new).and_return(fake_s3)
allow(fake_s3).to receive(:put_object)
uploader.upload_file(test_key, 'file_path', record=true)
expect(fake_s3).to have_received(:put_object).with(
{bucket: bucket, key: test_key, body: 'text'})
end

wrong redirect with capybara

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))

Resources