Accessing controller instance variables with Minitest - ruby

I'm trying to access the instance variables inside my controllers with minitest.
For example:
microposts_controller.rb:
def destroy
p "*"*40
p #cats = 42
end
How would I test the value of #cats with inside microposts_controller_test.rb with minitest?
I know I can submit the delete request from the browser and check my server logs and find:
"****************************************"
42
I read in another answer that I have access to an assigns hash with all the instance variables but it didn't work. I've also tried looking inside the controller object. Shown below:
microposts_controller.rb:
test "#cats should exist in destroy method" do
delete micropost_path(#micropost)
p controller.instance_variables
p assigns[:cats]
end
output:
[:#_action_has_layout, :#_routes, :#_request, :#_response, :#_lookup_context, :#_action_name, :#_response_body, :#marked_for_same_origin_verification, :#_config, :#_url_options]0:04
nil
I was expecting to see the #cats instance variable inside the controller object. I was also expecting to see 42 being output.
What am I missing here?

You can use view_assigns:
# asserts that the controller has set #cats to true
assert_equal #controller.view_assigns['cats'], true

I had a before_action that checks to make sure the user is logged in, so the delete request was getting redirected.
I also have a test helper that will put a valid user id into the session. Using that everything works as expected :)
microposts_controller_test.rb:
test "#cats should exist?" do
log_in_as(users(:michael))
delete micropost_path(#micropost)
p controller.instance_variables
p assigns[:cats]
end
test_helper.rb:
def log_in_as(user)
session[:user_id] = user.id
end
output:
[:#_action_has_layout, :#_routes, :#_request, :#_response, :#_lookup_context, :#_action_name, :#_response_body, :#marked_for_same_origin_verification, :#_config, :#current_user, :#_params, :#micropost, :#cats, :#_url_options]
42

Related

How can I export existing AWS ELB policies? undefined method 'reduce'

We want to export our ELB configurations for re-use. I can get the ELB configs with:
all_elbs = Fog::AWS::ELB.load_balancers.all()
But this returns a failure:
all_policies = Fog::AWS::ELB.policies.all()
#=> /Library/Ruby/Gems/2.0.0/gems/fog-aws-0.0.6/lib/fog/aws/models/elb/policies.rb:20:
#=> in `munged_data': undefined method `reduce' for nil:NilClass (NoMethodError)
Ultimately, I want to be able to recreate a ELB based on an existing ELB.
That error message means that on line 20 of policies.rb there is code like foo.reduce and foo happens to be nil.
If we look at the source code of the gem, we see:
def munged_data
data.reduce([]){ |m,e| # line 20
So, the problem is that data is nil when the munged_data method is called. We see on line 8 of the same file that data is defined via a simple attr_accessor call. I cannot tell for sure where that should have been set. (There are 227 instances of #data = or data = in the gem.) This seems like a bug in the AWS gem, unless you were supposed to call some method before calling .all on policies.
Tracing further, we see that policies is defined in load_balancer.rb on line 154 as:
def policies
Fog::AWS::ELB::Policies.new({
:data => policy_descriptions,
:service => service,
:load_balancer => self
})
end
Assuming that the data passed to the method is used directly as the #data instance variable, then the problem is that policy_descriptions returned nil.
The implementation of policy_descriptions is:
def policy_descriptions
requires :id
#policy_descriptions ||= service.describe_load_balancer_policies(id).body["DescribeLoadBalancerPoliciesResult"]["PolicyDescriptions"]
end
If service.describe_load_balancer_policies(id).body["DescribeLoadBalancerPoliciesResult"] returned nil (or any object that did not have a [] method) this method would have thrown an error. So, my deduction is that this returned something like a hash, but that hash has no "PolicyDescriptions" key.
From there...I don't know.

Can't access current_user inside .new do block in the ApplicationController

I'm using devise and the bitbucket api gem and I have a method in my ApplicationController which creates an instance so I can make API calls. To do that, it tries to read the token and secret from the current_user.
This works fine with hardcoded token and secret strings, I'm also able to do puts current_user.inspect before the do block, and that all works fine. I'm also sure that bb_token and bb_secret exist (I'm able to call puts on them individually).
But once I try to create my bitbucket instance, it can't read current_user anymore. Any ideas?
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user
def bitbucket
puts "token----------"
puts current_user
#bitbucket = BitBucket.new do |config|
config.oauth_token = current_user.bb_token # replaceing this with hardcoded string works
config.oauth_secret = current_user.bb_secret # replaceing this with hardcoded string works
config.client_id = 'xx'
config.client_secret = 'yy'
config.adapter = :net_http
end
end
end
And the error:
NameError (undefined local variable or method `current_user' for #<BitBucket::Client:0x007fbebc92f540>):
app/controllers/application_controller.rb:12:in `block in bitbucket'
app/controllers/application_controller.rb:11:in `bitbucket'
It seems block passed to BitBucket.new is executed in context of new BitBucket::Client instance (BitBucket.new is really BitBucket::Client.new, according to this).
A glance to the source confirms this supposition.
If you want to pass current_user, you can recall that the blocks are closures, so they keep the context in which they are defined. So you can do something like this:
def bitbucket
# (...)
user = current_user # local variable assignment
#bitbucket = BitBucket.new do |config|
config.oauth_token = user.bb_token # it works because user is local variable and the block is closure
# (...)
end
end
Inside BitBucket.new do..end block,self is set to config. But current_user is not a instance method of BitBucket class. Thus a valid error is thrown.

Is there a way to mock/stub "puts" in Rails

I am printing some custom messages in my application using the puts command. However, I do not want these to be appearing in my Test Output. So, I tried a way to stub puts as shown below. But it still outputs my messages. What am I doing wrong ?
stubs(:puts).returns("") #Did not work out
Object.stubs(:puts).returns("") #Did not work out either
puts.stubs.returns "" #Not working as well
Kernel.stubs(:puts).returns "" #No luck
I am using Test::Unit
You probably need to stub it on the actual instance that calls puts. E.g. if you're calling puts in an instance method of a User class, try:
user = User.new
user.stubs(:puts)
user.some_method_that_calls_puts
This similarly applies to when you're trying to test puts in the top-level execution scope:
self.stubs(:puts)
What I would do is define a custom log method (that essentially calls puts for now) which you can mock or silence in test quite easily.
This also gives you the option later to do more with it, like log to a file.
edit: Or if you really want to stub puts, and you are calling it inside an instance method for example, you can just stub puts on the instance of that class.
Using Rails 5 + Mocha: $stdout.stubs(puts: '')
So the comments to the original post point to the answer:
Kernel.send(:define_method, :puts) { |*args| "" }
Instead of silencing all output, I would only silence output from the the particular objects that are putsing during your tests.
class TestClass
def some_method
...
puts "something"
end
end
it "should do something expected" do
TestClass.send(:define_method, :puts) { |*args| "" }
test_class.some_method.should == "abc123"
end

What does Sinatra::Base.condition actually do?

I've come across the sinatra condition method and am puzzled in how it works.
I have a piece of code:
def auth user
condition do
redirect '/login' unless user_logged_in?
end
end
Which checks to see if a user is logged for certain routes, an example route:
get '/', :auth => :user do
erb :index
end
The method user_logged_in? is defined in a helper file in the lib directory of the project:
def user_logged_in?
if session[:user]
#user = session[:user]
return #user
end
return nil
end
So, the question is:
How does the condition block know what the session[:user] contains, when at the get '/' route the session[:user] hasn't even been set?
The condition method is defined in the following GitHub page: sinatra base condition method
Thanks.
When you define a route, the key of each member of the options hash is called as a method, with the value passed as the arguments.
So when you do get '/', :auth => :user do ... the method auth gets called with the argument :user. This in turn calls the condition method with the block.
The condition method is actually defined just above where you link to which is a usage of it. It looks like this:
def condition(name = "#{caller.first[/`.*'/]} condition", &block)
#conditions << generate_method(name, &block)
end
The generate_method method converts the block into a method with the given name, and then this method is saved in the #conditions array. The contents of #conditions are then saved with the definition of the route, and #conditions is cleared ready for the next route definition.
At this point, the block of code passed to condition hasn't been executed. It has in effect been saved for later.
When an actual request comes in, if the request path matches the route, then each condition associated with that route is executed to check that it is fulfilled. In this example, this is when redirect '/login' unless user_logged_in? is first executed, so the session will have been set up and session[:user] will be available (or not if they're not logged in).
The important thing to understand about this is that when you pass a block to a method, the code in that block is not necessarily called right away. In this case the code in the block is only called when an actual request arrives.
Because Sinatra is responsible for calling both the condition methods and the route methods. Therefore, it should be safe to assume that whatever is set when your route method executes is also set when your condition execute.
Take a look at the code starting here: conditions are called one by one; if all conditions match, then the block gets called. Nothing much happens between checking conditions and calling the block: they are basically run with the same context.

Shoulda: How would I use an instance variable outside of a setup or should block?

I'm trying to do something like the following:
#special_attributes = Model.new.methods.select # a special subset
#special_attributes.each do |attribute|
context "A model with #{attribute}" do
setup do
#model = Model.new
end
should "respond to it by name" do
assert_respond_to #model, attribute
end
end
end
However, #special_attributes is out of scope when running the unit tests, leaving me with a nil object on line 2. I can't figure out where/how to define it to bring it in scope. Any thoughts?
Got it (I think). Shoulda is executing the block in the context of Shoulda::Context. In the above case, #special_attributes is an instance variable of my test class, not Shoulda::Context. To fix this, instead of using instance variables, just use local variables in the context block.
So, for example:
context "Model's" do
model = Model.new
special_attributes = model.methods.select # a special subset
special_attributes.each do |attribute|
context "attribute #{attribute}" do
setup do
#model = model
end
should "should have a special characteristic"
assert_respond_to #model, attribute
...
end
end
end
end

Resources