Rails 4 - unit test failing to create model object - unit-testing

I want to unit test an application using shoulda.
In the test i'm doing
User.create!(name: "James")
When i run the test i'm getting the following error:
ActiveRecord::StatementInvalid: Mysql2::Error: Field 'name' doesn't have a default value: INSERT INTO `users` (`created_at`, `updated_at`, `id`) VALUES ('2014-04-07 12:03:07', '2014-04-07 12:03:07', 980190962)
Has this something to do with rails 4 strong parameters?
How can i solve this?

Check if you have invalid fixtures laying around. Try deleting/fixing test/fixtures/users.yml
Note: You should get full stacktraces in the unit tests by disabling the backtrace silencers in config/initializers/backtrace_silencers.rb.

Has this something to do with rails 4 strong parameters?
No, Strong Parameters have nothing to do with this because you are creating your object directly from a hash and not from any ActionController::Parameters.
The issue comes from somewhere else (probably your database).
As I can see from this article, a solution to your problem could be to migrate your field like that:
change_column :users, :name, :string, :limit => 255, :null => false, :default => ''

I got similar issue while running unit tests. This may happen if your mysql DB connection is in strict mode. Disable strict mode by adding setting in database.yml as below and try.
test:
database:
host: localhost
name: test_db
username: username
password: password
strict: false

Related

No route matches {:action=>"show" ... missing required keys: [:id]

Seeing many question related to this but none of them gives answer to my problem.
I have Rails api application without ActiveRecord support. It is easy to reproduce problem. Follow steps:
Create rails api site without ActiveRecord support
rails new test001 -O --api
Change folder to test001 and run:
rails g scaffold testapp id name
Create model file testapp.rb in app/models folder
class Testapp
include ActiveModel::Model
attr_accessor :id, :name, :created_at, :updated_at
def self.all
t1 = Testapp.new(:id =>111, name: "t111")
return [t1]
end
end
Start server
rails s
Using postman REST client create GET request
http://localhost:3000/testapps.json
It fails with error ActionView::Template::Error (No route matches {:action=>"show", :controller=>"testapps", :format=>:json, :id=>#<Testapp:0x00000005411518 #id=111, #name="t111">} missing required keys: [:id]):
I have dummy implementation for POST, PUT, GET 1 item and all works. Here is dummy implementation of GET 1 item (/testapps/x.json)
def self.find(p)
return Testapp.new(:id =>p, name: "t123")
end
What is the problem with GET all (/testapps.json)?
Found solution.
Problem is scaffold generated index.json.jbuilder
file:
json.array!(#testapps) do |testapp|
json.extract! testapp, :id, :id, :name
json.url testapp_url(testapp, format: :json) #REMOVE
end
It added line json.url testapp_url(testapp, format: :json) for no reason. json.extract! deserialized object already.
Removing line solved problem.
I still do not know why testapp_url(testapp, format:json) caused error. Checking Rails Routing document http://guides.rubyonrails.org/routing.html

RSpec Controller Spec: expecting <"template"> but rendering with <[]>

I think that I am running a fairly normal spec using rspec, devise, rails 4:
fit "renders the sysadmin view if user is a system_admin" do
user = build(:user)
user.skip_confirmation!
user.save
user.update_attribute(:system_admin, true)
sign_in user
subject {get :index, format: :html}
expect(subject).to render_template(:sysadmin)
end
but no template seems to be getting rendered and I am receiving:
expecting <"sysadmin"> but rendering with <[]>
. My best guess is that I am missing something in the interplay with devise. I followed the code here:
https://github.com/plataformatec/devise/wiki/How-To:-Stub-authentication-in-controller-specs
for stubbing devise in this spec.

validate uniqueness in rails4 and case sensitivity fails

validates :name, uniqueness: true
The above validates name with case sensitive uniqueness. Any other default validators/options exists to include to case-insensitive checking.
Please help.
Thanks in advance.
I found this code here: https://stackoverflow.com/a/6987482/2754188
You can use this line:
validates :name, uniqueness: { case_sensitive: false }
If you're using a text-column, then the following should easily work:
validates_uniqueness_of :name
The default setting for case_sensitivity is :true and you can even add the following to your validation:
validates_uniqueness_of :name, :case_sensitive => false
This setting is however ignored by non-text columns.
If you are working on uniqueness of a record in Rails app, then please be reminded about this Rails article which says that Rails uniqueness is not fool proof. Scroll down to the bottom of this article Rails - Concurrency and integrity issues to know in detail.
In short, duplicates can still occur during concurrent operations.
I have faced these issue of duplicates in Rails app during concurrency and I had to apply a database level unique index on the table.

No route matches with valid object

Having read this:
"No route matches" error?
I'm trying to figure out if there is a gem or way to monkey patch actionpack to get around this constraint.
Basically, I'm writing specs (they run fast), and I don't understand why actionpack throws this error when being applied to an object which isn't "saved".
For two reasons:
Why is it throwing a "No route matches" when it really should be throwing something more meaningful (e.g. object must be saved before a route can be constructed, or object ID is nil). The exception seems a little obscure.
I shouldn't have to save the object at all if all I am trying to do is generate a url for that object, given the ID is populated using a factory or something similar.
This constraint makes it a pain to write fast tests, unless I'm missing something...
True, the error message is a bit obscure. Regarding your second point, you don't need to save an object to generate a URL, the helper will work just as well with a literal value.
building_path(1) # GET /buildings/1 => BuildingsController#show, params={:id=>"1"}
So in the example the object can be replaced with any value:
get :show, :id => "1"
For example, if you use rails generate scaffold Article, RSpec will build a spec like this:
def mock_article(stubs={})
(#mock_article ||= mock_model(Article).as_null_object).tap do |article|
article.stub(stubs) unless stubs.empty?
end
end
describe "GET show" do
it "assigns the requested article as #article" do
Article.stub(:find).with("37") { mock_article }
get :show, :id => "37"
assigns(:article).should be(mock_article)
end
end
which does not hit the database.

Adding validates_uniqueness_of to a model fails functional tests

Trying to make a simple application in rails 3.
If I create a team model with rails g scaffold team name:string && rake db:migrate, then run rake, I get success from the prebuilt tests.
If I simply add validates_uniqueness_of :name to the team model. The functional tests fail with
1) Failure:
test_should_create_team(TeamsControllerTest) [/test/functional/teams_controller_test.rb:20]:
"Team.count" didn't change by 1.
<3> expected but was
<2>.
I modified tests/fixtures/teams.yml to look like this:
one:
name: MyString
two:
name: MyString2
The test still fails.
It can't get much more basic than this; what have I missed?
Fixtures basically represent model instances that are in the database.
If you look at the top of test/functional/teams_controller_test.rb you'll see
setup do
#team = teams(:one)
end
and then in your failing functional test you'll have
post :create, :team => #team.attributes
This is what's happening: you're trying to create a new team with the same attributes as "the team fixture named :one". Since both would have the same name (since they have the exact same attributes), the uniqueness validation is failing.
Try replacing your setup block with this
setup do
#team = teams(:one)
#team.name = 'unique name'
end
Now you'll be creating a new team with the name 'unique name' (which isn't in the database according to the fixtures), and your test will pass.