I'm writing an app with Rails 3. In my functional test, test/functional/cust_infos_controller_test.rb, I have these:
require 'test_helper'
class CustInfosControllerTest < ActionController::TestCase
# Replace this with your real tests.
test "should get cust" do
get :customerinfo
assert_response :success
assert_not_nil assigns("cust_infos")
end
end
My controller is fairly straightforward as it just finds all the customer's info:
class CustInfosController < ApplicationController
def customerinfo
#cust_info = CustInfo.find(:all, :order=>"cust_id")
#cust_info.each do |ci|
if ci.upload_freq == 'W'
ci.upload_freq = 'Weekly'
elsif ci.upload_freq == 'M'
ci.upload_freq = 'Monthly'
end
end
end
end
When I ran it with:
$ ruby -Itest test/functional/cust_infos_controller_test.rb
I got the following error:
Loaded suite test/functional/cust_infos_controller_test
Started
E
Finished in 0.025258 seconds.
1) Error:
test_should_get_cust(CustInfosControllerTest):
ActiveRecord::StatementInvalid: PGError: ERROR: numeric field overflow
DETAIL: A field with precision 4, scale 0 must round to an absolute value less than 10^4.
: INSERT INTO "cust_infos" ("cust_id") VALUES (298486374)
1 tests, 0 assertions, 0 failures, 1 errors
In my cust_infos table, I have cust_id as integer. But I don't know why when I ran controller test to just get some record, active record will execute insert statement. How do I fix this?
UPDATE: I commented out all the lines in customerinfo method, and run the same test. Got the exact same result. So I'm guessing it's not my methods behavior, but rather Rails? Any hint?
I see what you are trying to do. What you need to do is create a helper for your view.
application_helper.rb
def display_upload_freq(s)
case s
when 'W'
'Weekly'
when 'M'
'Monthly'
end
end
cust_info_controller.rb
class CustInfosController < ApplicationController
def customerinfo
#cust_info = CustInfo.find(:all, :order=>"cust_id")
end
end
Then in your view when your are iterating through #cust_info, for upload_freq, use dislay_upload_freq(ci.upload_freq). Assuming you have #cust_info.each do |ci|. Then you won't be confusing the db with saving anything.
Are you using factories or fixtures to create your test data? If not, how are you creating it? My guess is that the insert is happening when your test data is being set up, not because of anything happening in your controller. The second part of that guess is borne out by the fact that commenting out all code in your controller isn't getting rid of the error.
I know this post is quite old, but I will post my answer in case someone is brought here by search engines.
I had the exact same problem, the issue originates from the fixture file(s) in test/fixtures/{name_of_your_model}.yml. Rails adds some initial values in this file, in my case it looked like this:
one: {}
# column: value
#
two: {}
# column: value
and when you want to run the test, it will try to create the test database using this fixtures. And that's where the issue occurs. Trying to create an empty record while you did not allow a null ID in your table.
Deleting these fixtures, or filling them with appropriate values should solve the problem.
Related
I'm new to rails and i've done a simple sorting of dates in descending order. and now i need to write a test for it. my controller looks like this
def index
#article = Article.all.order('date DESC')
end
i tried writing a test but it doesn't work this is my code
def setup
#article1 = articles(:one)
end
test "array should be sorted desc" do
sorted_array = article1.sort.reverse
assert_equal article1, sorted_array, "Array sorted"
end
You should write a better description, by saying what each part of the code refers to, like:
# this is my controller_whatever.rb
def index
#article = Article.all.order('date DESC')
end
#this is my test/controllers/controller_whatever_test.rb
def setup
#article1 = articles(:one)
end
...
In your case you didn't create a "sorting", you created a controller action that queries records in descending order, so to test it you either need a controller test or an integration test (controller tests I believe are being dropped of use in favour of integration ones), which are more complex, since you need to visit the path in the test, then assert that somehow your results are as expected.
I think the best way to do this is to actually create a scope for your model, use that when querying in index and then test that scope.
This would be something like:
# app/models/article.rb
scope :default -> { order(date: :desc) }
Which then you could test it with:
#test/models/article_test.rb
def setup
#articles = Article.all
end
test "should be most recently published first" do
assert_equal articles(:last), #articles.first
assert_equal articles(:first), #articles.last
end
And you would need two fixtures with different dates at least, but I would advise you to have 4 or 5 with different dates and written in a different order in the articles.yml file (to make sure the test passes because it's correct and not simply because of randomness), and change your index action to simply:
def index
#article = Article.all # since now you have a default_scope
end
(if you have other places where you query Articles and you need them ordered in another way, instead of a default_scope, create a particular one and use that, both on controller and model test)
I would write a functional test in a test class according to the controller of your index action.
I assume your controller is named ArticlesController then the test class name is ArticlesControllerTest placed in test/controllers/articles_controller_test.rb.
In the test method you call/request the index action of your controller and you check for a successful answer first. Then you catch the articles, which your controller returns in the #article1 instance variable, with assigns(:article1).
Now you could check your articles are set and you can check the dates. Here I loop through all articles in an easy way and compare the date of the article before is greater or equal to the date of the current article, because of the descending order. For a simple test it should be acceptable, because you should not have a big amount of test records. May be there's a better way to check the order.
class ArticlesControllerTest < ActionController::TestCase
test "index should provide sorted articles" do
get :index
assert_response :success
articles = assigns(:article1)
assert_not_nil articles
date = nil
articles.each do |article|
if date
assert date >= article.date
end
date = article.date
end
end
end
Read about Functional Tests for Your Controllers in the Rails 4.2 Guides for more information.
I have the test below and if there are any fixtures for this model it fails with total_unapproved and new_total being equal instead of new_total being one less.
If I remove loading the fixtures in test_helper.rb or comment them out it runs as I expect.
Here's the class function that sets approvals to true. It definitely works.
def InviteRequest.approve_invites(number)
inv_reqs = InviteRequest.where("approved = ?", false).first(number)
inv_reqs.each do |inv_req|
inv_req.approved = true
inv_req.save
inv_req.send_approved_email
end
end
Here's the test that calls the above function.
require 'test_helper'
class InviteRequestTest < ActiveSupport::TestCase
test "class method approve_invites(number) should approve 'number' InviteRequests" do
# ensure there is at least one instance
inv_req = InviteRequest.create(email: "hobojoe#test.com")
# set all InviteRequests.approved to false
InviteRequest.all.each {|r| r.approved = false; r.save}
total_unapproved = InviteRequest.where("approved = ?", false).count
Rails.logger.info "\nUnapproved Before: #{total_unapproved}"
InviteRequest.approve_invites(1)
new_total = InviteRequest.where("approved = ?", false).count
Rails.logger.info "Unapproved After: #{new_total}\n"
assert_equal total_unapproved - 1, new_total
end
end
Any idea why? I'm not using the fixtures in any other tests but maybe I will someday.
My fixtures weren't valid and changing them fixed the problem.
I'm still not exactly sure how things were failing though.
My fixtures looked like this:
one:
email: MyString
two:
email: MyString
This would fail my uniqueness validation and not save but I'm not sure why the newly created model wouldn't have 'approved' set to true and still be saved since it's correct.
Anyway, changing the fixtures to this fixed things.
one:
email: someguy#example.com
two:
email: somegirl#example.com
Maybe validation fails when you're calling InviteRequest#save? Try to replace save with save! and check to see if the test still passes.
When you use rails generator for new model, fixture file is generated as well looking like this:
one: {}
# column: value
two: {}
# column: value
When you run your tests using fixtures, rails tries to create this 2 empty records. If you have constraint in your migration (such as not null), your tests will fail for obvious reasons.
Solution:
Comment out this empty records in your fixtures files or fill them with something sensible.
First, I have a valid factory/model, and this particular test runs fine through the console.
model
validate :some_condition
def some_condition
errors.add(:attribute, "cannot be less than 5") if self.attribute < 5
end
test
it "should not allow values above 5" do
model = FactoryGirl.create(:model) # creates valid model
model.attribute = 10
model.valid?.should be_false
end
In the console:
model = FactoryGirl.create(:model)
model.attribute = 10
model.valid? # => false
In rspec
undefined method `<' for nil:NilClass
I cannot fathom why this is happening. It is obviously something to do with self.attribute, but why would it work in the console, yet not in the tests? attribute alone also returns same error, and I've checked, - self is defined as model instance. Regardless, this doesn't explain the inconsistency. It works in the console with exactly the same model and attributes.
To note: I have restarted all environments, this is based on a fresh reload.
update
In an act of desperation, I have outputted attribute in several contexts before this condition, and then exit. This has brought with it even stranger results. Work this out:
def some_condition
puts self.attribute # => returns blank in test, attribute value otherwise
puts "#{self.attribute}" # => returns attribute value in test!!!
exit
errors.add(:attribute, "cannot be less than 5") if self.attribute < 5
end
The above has made me incredibly tense. Do I now need tests to test my tests? really hope someone more experienced in ruby or the above tools has some logical explanation for this mess, because I'm completely lost.
It leads to this abomination:
errors.add(:attribute, "cannot be less than 5") if self.attribute < 5
# => IN TESTS self.attribute returns nil
errors.add(:attribute, "cannot be less than 5") if "#{self.attribute}".to_i < 5
# => IN TESTS self.attribute returns value! This works!?
Where do you even turn? Is it ruby, rails, factory girl, rspec?
FIX
After that massive wreck of a question, it turns out I forgot to rake db:test:prepare after a minor migration. I'm still baffled as to how it could have caused such an issue. Lesson learned. Run migrations across environments, and find a better debugger!
The RSpec syntax has changed a bit from versions 1 to 2 and that can muddle things up.
Could you tell me what happens if you write your test exactly like this?
it "should not allow values above 5" do
model = build(:model, :attribute => 10)
model.should_not be_valid
model.should have(1).error_on(:attribute)
end
The reason I'm using build rather than create is that way you can test your validations without hitting the database.
Consider the following class and methods: (This class is obviously much more complete, but for the sake of this thread...):
class Order < ActiveRecord::Base
def check
if (self.user.phone == "55555555") do
self.a_certain_method
return
end
end
def a_certain_method
# Real implementation goes here
end
end
And the following Unit Test:
describe :do_route do
it "should call a_certain_method if user phone number matches 55555555" do
# Create a user
user = Factory(:user)
# Set hard-coded phone number
user.phone = "55555555"
user.save!
# Create an order made by the ordering user
order = Factory(:order, :ordering_user => user)
# Set expectation for a "a_certain_method" call
mock(order).a_certain_method
# Call the tested method
order.check
end
end
From some reason, the above test produces an RR::Errors::TimesCalledError error, which claims that a_certain_method was called 0 times instead of 1... I've been searching around the web for a solution with no luck.
I've tried building a similiar test on a non-activerecord class, and the test produces no errors.
I've used the debugger to check that it does reach the self.a_certain_method line, and also tried using the following instead of mock(order).a_certain_method:
any_instance_of(Order) do |o|
mock(o).a_certain_method
end
Does anyone have any idea how to solve this issue since i'm kind of desperate...
I figured out what the problem was, it failed since the number was already in the database. so it failed to save the hard coded user.phone change.
Thanks for the help though :)
I have a test more or less like this:
class FormDefinitionTest < ActiveSupport::TestCase
context "a form_definition" do
setup do
#definition = SeedData.form_definition
# ...
I've purposely added a
raise "blah"
somewhere down the road and I get this error:
RuntimeError: blah
test/unit/form_definition_test.rb:79:in `__bind_1290079321_362430'
when I should be getting something along:
/Users/pupeno/projectx/db/seed/sheet_definitions.rb:17:in `sheet_definition': blah (RuntimeError)
from /Users/pupeno/projectx/db/seed/form_definitions.rb:4:in `form_definition'
from /Users/pupeno/projectx/test/unit/form_definition_test.rb:79
Any ideas what is sanitizing/destroying my backtraces? My suspicious is shoulda because the when the exception happens inside a setup or should is whet it happens.
This is a Rails 3 project, in case that's important.
That is because the shoulda method #context is generating code for you. for each #should block it generates a completely separate test for you so e.g.
class FormDefinitionTest < ActiveSupport::TestCase
context "a form_definition" do
setup do
#definition = SeedData.form_definition
end
should "verify some condition" do
assert something
end
should "verify some other condition" do
assert something_else
end
end
end
Then #should will generate two completely independent tests (for the two invocations of #should), one that executes
#definition = SeedData.form_definition
assert something
and another one that executes
#definition = SeedData.form_definition
assert something_else
It is worth noting that it does not generate one single test executing all three steps in a sequence.
These generated blocks of codes have method names like _bind_ something and the generated test have name that is a concatenation of all names of the contexts traversed to the should block plus the string provided by the should block (prefixed with "should "). There is another example in the documentation for shoulda-context.
I think this will give you the backtrace that you want. I haven't tested it, but it should work:
def exclude_backtrace_from_location(location)
begin
yeild
rescue => e
puts "Error of type #{e.class} with message: #{e.to_s}.\nBacktrace:"
back=e.backtrace
back.delete_if {|b| b~=/\A#{location}.+/}
puts back
end
end
exclude_backrace_from_location("test/unit") do
#some shoulda code that raises...
end
Have you checked config/initializers/backtrace_silencers.rb? That is the entry point to customize that behavior. With Rails.backtrace_cleaner.remove_silencers! you can cleanup the silencers stack.
More informations about ActiveSupport::BacktraceCleaner can be found here.