RSpec test method is called on `main` object
Clash Royale CLAN TAG#URR8PPP
RSpec test method is called on `main` object
Sometimes we call methods on the ruby main
objects. For example we call create
for FactoryBot and we call _()
for I18n.
main
create
_()
What's a proper way to test these top level methods got called in RSpec?
For example, I want to test N_
is called, but it would not work because the self in Rspec and self in the file are different.
N_
# spec
describe 'unfound_translations' do
it 'includes dynamic translations' do
expect(self).to receive(:N_)
load '/path/to/unfound_translations.rb')
end
end
# unfound_translations.rb
N_('foo')
However this does not pass.
_N
it
Sorry I was unclear. The example I gave does not work.
– lulalala
Aug 8 at 8:14
I think I understand the question, but please provide a Minimal, Complete, and Verifiable example. A minimal example should not contain anything like
load Rails.root.join('locale/unfound_translations.rb')
, and a complete, verifiable example should be an actual reproduction of the problem.– Tom Lord
Aug 8 at 8:21
load Rails.root.join('locale/unfound_translations.rb')
@lulalala Are you going to fix or close your question?
– meta
Aug 9 at 8:02
@meta I edited my question when Tom mentioned it. However
load
is essentially what I need (I need to repeatedly load that file in order to perform different tests) so it is still there.– lulalala
Aug 10 at 1:22
load
1 Answer
1
Ok, I get your problem now. Your main issue is that self
in it
block is different that self
inside unfound_translations.rb
. So you're setting expectations on one object and method N_
is called on something completely different.
self
it
self
unfound_translations.rb
N_
(Edit: I just realized, when reading the subject of this question again, that you already was aware of it. Sorry for stating the obvious... leaving it so it may be useful to others)
I managed to have a hacky way that is working, here it is:
# missing_translations.rb
N_('foo')
and the spec (I defined a simple module for tests inside it for simplicity):
module N
def N_(what)
puts what
end
end
RSpec.describe 'foo' do
let(:klass) do
Class.new do
extend N
end
end
it do
expect(klass).to receive(:N_)
klass.class_eval do
eval(File.read('missing_translations.rb'))
end
end
end
What it does it's creating an anonymous class that. And evaluating contents of missing_translations.rb
inside means that klass
is the thing
that receives N_
method. So you can set expectations there.
missing_translations.rb
klass
thing
N_
I'm pretty sure you can replace extend N
module with whatever module is giving you N_
method and this should work.
extend N
N_
It's hacky, but not much effort so maybe good enough until more elegant solution is provided.
thanks, I was also thinking
eval
as last resort. Would still be nice if somehow I can obtain the main object :P– lulalala
Aug 10 at 11:27
eval
There's also Kernel#binding but this also is to be used with
eval
.– meta
Aug 10 at 11:41
eval
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Hold on! How is it supposed to pass if you call
_N
outside ofit
block?– meta
Aug 8 at 8:10