Ruby/Rails: eval doesn't seems to work - ruby-on-rails

Am trying to evaluating the below code using rspec.
Given :
# book = ...
Rails.logger.info book.inspect
The above code prints the value of return type is boolean i.e {:foo=>false}
eval(book[:foo]).should be_false
but that doesn't seem to work. While trying to run rspec, it throws the following exception:
Failure/Error: eval(book[:foo]).should be_false
TypeError:
can't convert false into String
So, how can i evaluate a boolean to a method, such as my final result would be the equivalent ?

eval executes passed argument interpreting it as Ruby code. What Ruby code do you think is contained in false object?
eval(false) # cannot execute false object
eval("false") # executes a string and returns false object
see the difference?
i don't know what exactly are you testing but you could simply try
book[:foo].should be_false

Related

Rspec: Checking the content of a system call

I have a Rake task in my Rails project which executes openssl through a system call.
The code looks like this:
system('bash', '-c', 'openssl cms -verify...')
I need to run the command in bash rather than dash (which is default on Ubuntu) to use process substitution in the command.
I need to create a test with rspec which checks that, in this case, the argument verify was passed as expected.
I have tried the following:
expect(Kernel).to receive(:system) do |args|
expect(args[2]).to match(/verify/)
end
However, this only gives me the third letter in the first string sent to system - i.e. the letter s from bash - rather than the third argument sent in the system call.
What am I doing wrong here? Any suggestions would be much appreciated.
Args are being passed to the block as sequential arguments, so if you want to treat them as an array, you need a splat operator in do |*args|:
expect(Kernel).to receive(:system) do |*args|
expect(args[2]).to match(/verify/)
end
Just to take a step back, it's important to understand how block arguments work, since they are different from methods. For example:
def my_fn(*args)
yield(*args)
end
my_fn(1,2,3) { |args| print args }
# => 1
my_fn(1,2,3) { |a, b, c| print [a,b,c] }
# => [1,2,3]
my_fn(1,2,3) { |*args| print args }
# => [1,2,3]
So if you did do |args| (without the splat), you are assigning the args variable to the first argument passed to the block ("bash") and ignoring the other arguments.

Vim global variable check throws error in lua (neovim plugin development)

I'm trying write a neovim plugin using lua, when checking if a variable exists, lua throws an error like: Undefined variable: g:my_var
Method 1:
local function open_bmax_term()
if (vim.api.nvim_eval("g:my_var")) then
print('has the last buff')
else
print('has no the last buff')
end
end
Method 2:
local function open_bmax_term()
if (vim.api.nvim_get_var("my_var")) then
print('has the last buff')
else
print('has no the last buff')
end
end
this is a similar function written in viml which does works: (this does not throw any error)
fun! OpenBmaxTerm()
if exists("g:my_var")
echo "has the last buff"
else
echo "has no the last buff"
endif
endfun
any idea how to get this working in lua? I tried wrapping the condition inside a pcall which had an effect like making it always truthy.
vim.api.nvim_eval("g:my_var") just evaluates a vimscript expression, so accessing a non-existent variable would error just like in vimscript. Have you tried vim.api.nvim_eval('exists("g:my_var")') instead?
Edit: Using vim.g as #andrewk suggested is probably the better solution as using the dedicated API is more elegant than evaluating strings of vim script.
You can use the global g: dictionary via vim.g to reference your variable:
if vim.g.my_var == nil then
print("g:my_var does not exist")
else
print("g:my_var was set to "..vim.g.my_var)
end
You can reference :h lua-vim-variables to see other global Vim dictionaries that are available as well!

Trouble mocking `Resolv::DNS.open`

I'm trying to mock the code below using MiniTest/Mocks. But I keep getting this error when running my test.
Minitest::Assertion: unexpected invocation: #<Mock:0x7fa76b53d5d0>.size()
unsatisfied expectations:
- expected exactly once, not yet invoked: #<Mock:0x7fa76b53d5d0>.getresources("_F5DC2A7B3840CF8DD20E021B6C4E5FE0.corwin.co", Resolv::DNS::Resource::IN::CNAME)
satisfied expectations:
- expected exactly once, invoked once: Resolv::DNS.open(any_parameters)
code being tested
txt = Resolv::DNS.open do |dns|
records = dns.getresources(options[:cname_origin], Resolv::DNS::Resource::IN::CNAME)
end
binding.pry
return (txt.size > 0) ? (options[:cname_destination].downcase == txt.last.name.to_s.downcase) : false
my test
::Resolv::DNS.expects(:open).returns(dns = mock)
dns.expects(:getresources)
.with(subject.cname_origin(true), Resolv::DNS::Resource::IN::CNAME)
.returns([Resolv::DNS::Resource::IN::CNAME.new(subject.cname_destination)])
.once
Right now you are testing that Resolv::DNS receives open returns your mock but
since you seem to be trying to test that the dns mock is receiving messages you need to stub the method and provide it with the object to be yielded
Try this instead:
dns = mock
dns.expects(:getresources)
.with(subject.cname_origin(true), Resolv::DNS::Resource::IN::CNAME)
.once
::Resolv::DNS.stub :open, [Resolv::DNS::Resource::IN::CNAME.new(subject.cname_destination)], dns do
# whatever code actually calls the "code being tested"
end
dns.verify
The second argument to stub is the stubbed return value and third argument to stub is what will be yielded to the block in place of the original yielded.
In RSpec the syntax is a bit simpler (and more semantic) such that:
dns = double
allow(::Resolv::DNS).to receive(:open).and_yield(dns)
expect(:dns).to receive(:getresources).once
.with(subject.cname_origin(true), Resolv::DNS::Resource::IN::CNAME)
.and_return([Resolv::DNS::Resource::IN::CNAME.new(subject.cname_destination)])
# whatever code actually calls the "code being tested"
You can write more readable integration tests with DnsMock instead of stubbing/mocking parts of your code: https://github.com/mocktools/ruby-dns-mock

How to save a variable in an rspec expect?

I have something along the following lines in one of my spec files:
expect(my_instance).to receive(:my_function).with(arg: instance_of(String))
I want to be able to capture the actual value of arg in a variable I can use in the spec. Is there a way to do that? I checked the rspec docs but didn't find anything like that.
You could declare the variable, say captured_arg before the expect (or allow, if you don't want it to fail if my_instance does not receive my_function). Then you can collect the arguments in a block and set captured_arg within that block.
captured_arg = nil
expect(my_instance).to receive(:my_function) { |arg| captured_arg = arg }
Edit: (Keyword Arguments)
If you are using keyword arguments, just modify the script above slightly, using arg as the keyword argument you'd like to capture:
captured_arg = nil
expect(my_instance).to receive(:my_function) { |args| captured_arg = args[:arg] }

How to test log content with RSpec?

For example, it generated a log(my_work.log) content as:
I, [2015-05-14T00:00:00.000000 #5590] INFO -- : Work started.
I want to test if my_work.log has content Work started., how to do?
I don't want to match all line include datetime, because that contains #5590, I can't stub it.
You can pass in an instance of StringIO when initializing Logger to capture the output and then match on the expected content:
require 'logger'
describe "log" do
let(:log) { StringIO.new }
let(:logger) { Logger.new(log) }
let(:expected) { "Work started" }
it "should add the expected content to the log" do
logger.info(expected)
log.rewind
expect(log.read).to match(/.+#{expected}$/)
end
end
Rails.logger uses some methods to log things, for example:
debug
info
fatal
warn
So in your case you use info, to log something, instead loking for a match, you can detect if method info was called:
it 'logs exception' do
# some code
expect(Rails.logger).to receive(:info)
# execute the line that logs something
end
Even you can add parameters to receive method with reserved word with:
expect(Rails.logger).to receive(:info).with('Work started.')
This cause you need to specify something
Check rspec and rails logger
Also check this stackoverflow post
With RSpec's output matcher (introduced in 3.0) you can do the following:
expect { my_method }.to output("my message").to_stdout
expect { my_method }.to output("my error").to_stderr
In case of libraries such as Logger or Logging you may have to use output.to_<stdout/stderr>_from_any_process.
It's simple, clean and will test whether your messages actually reach the output.

Resources