RSpec test method is called on `main` object

The name of the pictureThe name of the pictureThe name of the pictureClash 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.





Hold on! How is it supposed to pass if you call _N outside of it block?
– meta
Aug 8 at 8:10


_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.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard