Adding validates_uniqueness_of to a model fails functional tests - unit-testing

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.

Related

Capybara Poltergeist feature test failing on CI but passes locally

I'm having weird issues with some of my feature tests using Capybara with poltergeist driver.
The test should perform a simple checkout in my online shop.
They all pass fine on my local MacBook as well as on an Ubuntu vagrant box. However on CI services like Codeship, Wercker or Semaphore they fail with the very same error.
My spec:
require 'rails_helper'
describe 'Checkout' do
let!(:product) { FactoryGirl.create(:product) }
it 'checks out via CreditCard', js: true do
visit products_path
expect(page.body).to have_link('Test Product 1')
click_link('Test Product 1')
#rest of spec ommitted
end
end
The error I get on CI is:
2) Checkout checks out via CreditCard
Failure/Error: click_link('Test Product 1')
Capybara::ElementNotFound:
Unable to find link "Test Product 1"
To me this is super weird, as the first expectation 'expect(page.body).to have_link('Test Product 1')' seems to pass but then it fails on the next step where it should actually click the link it just assured to be present on the page?
I then reconfigured poltergeist driver as follows to gather more debug information.
Snippet of rails_helper.rb:
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, {js_errors: false,
#inspector: true,
phantomjs_logger: Rails.logger,
logger: nil,
phantomjs_options: ['--debug=no', '--load-images=no', '--ignore-ssl-errors=yes', '--ssl-protocol=TLSv1'],
debug: true
})
end
Capybara.server_port = 3003
Capybara.app_host = 'http://application-test.lvh.me:3003' # lvh.me always resolves to 127.0.0.1
Capybara.javascript_driver = :poltergeist
Capybara.current_driver = :poltergeist
Capybara.default_wait_time = 5
Now I can see on CI console that the test successfully visits my products_path and the expected html page (including my the link it should click) is being returned.
I removed the rest of the HTML response to make it more readable:
{"name"=>"visit", "args"=>["http://application-test.lvh.me:3003/products"]}
{"response"=>{"status"=>"success"}}
{"name"=>"body", "args"=>[]}
{"response"=>"--- snip --- <div class=\"info\">\n<a class=\"name color-pomegranate\" href=\"/en/products/6\">\nTest Product 1\n</a>\n850,00 \n</div> --- snap ---"}
{"name"=>"find", "args"=>[:xpath, ".//a[./#href][(((./#id = 'Test Product 1' or normalize-space(string(.)) = 'Test Product 1') or ./#title = 'Test Product 1') or .//img[./#alt = 'Test Product 1'])]"]}
{"response"=>{"page_id"=>4, "ids"=>[0]}}
{"name"=>"visible", "args"=>[4, 0]}
{"response"=>false}
{"name"=>"find", "args"=>[:xpath, ".//a[./#href][(((./#id = 'Test Product 1' or contains(normalize-space(string(.)), 'Test Product 1')) or contains(./#title, 'Test Product 1')) or .//img[contains(./#alt, 'Test Product 1')])]"]}
{"response"=>{"page_id"=>4, "ids"=>[1]}}
{"name"=>"visible", "args"=>[4, 1]}
{"response"=>false}
The last two find actions repeat until Capybara reaches its timeout, then the test fails.
I double checked the xpath Capybara uses via some online xpath validators, but as expected it matches the HTML link.
I also used capybara-screenshot gem to dump the HTML body on failure and the link in question is also present.
So why is the test still failing?
Is there any race condition that I'm not aware of? Why is it passing locally but on none of the CI services?
Here are my gem version:
capybara (2.4.4)
capybara-screenshot (1.0.3)
database_cleaner (1.3.0)
factory_girl (4.5.0)
factory_girl_rails (4.5.0)
poltergeist (1.5.1)
rails (4.1.8)
rspec (3.1.0)
rspec-rails (3.1.0)
and phantomjs 1.9.7
While I can't reproduce this, I remember having this problem before. I believe your line:
expect(page.body).to have_link('Test Product 1')
is passing because the link is literally on the body of the html page, even though it may be hidden due to CSS or JS behavior. However, the line:
click_link('Test Product 1')
definitely checks for visibility before clicking the link. You should check your spec_helper.rb configurations to make sure:
Capybara.ignore_hidden_elements = true
is present, so that the first line wouldn't pass. I think I also had to change the first line I mentioned to:
# Change page.body to page, to look at the rendered page, not the literal one
expect(page).to have_link('Test Product 1')
Once you do this, the first line blocks the thread and waits until the link becomes visible. Then the rest of the test will pass.
Hope this solves it.

Rails 4 - unit test failing to create model object

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

Minitest - test class in Rails 4

I am trying to use Minitest in a fresh Rails 4 install. My understanding is that if I have a class that doesn't inherit from ActiveRecord then I should be able to use Minitest itself, without Rails integration:
#test/models/blog.rb
require "minitest/autorun"
class Blog < Minitest::Unit::TestCase
def setup
#b = Blog.new
end
def test_entries
assert_empty "message", #b.entries
end
#app/models/blog.rb
class Blog
attr_reader :entries
def initialize
#entries = []
end
I run the test with ruby test/models/blog.rb.
My problem comes with the setup method. If I don't include an entry for my blog, the tests fails with the message that there are the wrong number of arguments in setup. If I include an entry in my setup message #b = Blog.new entries: "Twilight", my test fails in the test_entries method because entries is an undefined method.
You have a couple problems. First, you are not requiring "test_helper", which means that rails isn't getting loaded when you run this test, which means that the mechanism rails uses to resolve missing constants isn't loaded. You will either need to require the helper or require the blog file directly. Second, you are overwriting the constant you want to test with the test, which is why you are getting confusing messages. Name the test class BlogTest instead to avoid this.
This is what I think you are trying to do:
require "minitest/autorun"
require "models/blog" # Assuming "app" is in your load path when running the test
#require "test_helper" # Or require this instead if you need to use DB
class BlogTest < Minitest::Unit::TestCase
def setup
#b = Blog.new
end
def test_entries
assert_empty #b.entries, "Blog entries should be empty"
end
end

Rails: Invalid single-table inheritance type error

So, I am working on migrating this php site with an existing database which I cannot change over to Rails. There is a table: Quotes with a column named type. Whenever I try and create a model of this and set the type, it tells me the following error:
ActiveRecord::SubclassNotFound (Invalid single-table inheritance type: HOME is not a subclass of Quotes)
I don't understand why it thinks its inheriting because it's not supposed to. My create method looks like this:
quote = Quotes.create(
agent_id: agent.id,
client_id: client.id,
type: 'HOME',
status: 0,
date_created: DateTime.now
)
If I comment out the type, everything works fine. But with the Type it errors.
I resolved this by setting the models inheritance_column to nil. Active Record Models can inherit from a table through the attribute :type, setting the inheritance_column to nil removes that attribute allowing you to have a database column named type
class Quote < ActiveRecord::Base
self.inheritance_column = nil
end
I hate having potential gotchas deep in the code especially in the intial processes like generating a model. Better to just change the reserved word to something else and free yourself up to take advantage of inheritance column later if the need comes up. A cleaner solution is listed here -> rename a database column name using migration
It reads;
Execute $> rails generate migration ChangeColumnName
where, ChangeColumnName is the name of our migration. This can be any name.
Now, edit the generated migration file at db/migrate/_change_column_name.rb
class ChangeColumnName < ActiveRecord::Migration
def change
rename_column :table_name, :old_column, :new_column
end
end
$> rake db:migrate
You will have to edit controller and view files e.g. if the model name is Product then you will likely edit these files
/app/views/products/_form.html.erb
/app/views/products/show.html.erb
/app/controllers/products_controller.erb
/app/views/products/index.html.erb

Using machinist instead of fixtures

In my Rails 3 application, I have a User model with the following fields
name: string
email: string
children: has_many association to another model
I'm using machinist 2 to generate mock data, its blueprint looks like
User.blueprint do
name { 'user{sn}' }
email { '{object.name}#domain.com' }
end
And User's Unit Test:
require 'test_helper'
class UserTest < ActiveSupport::TestCase
should have_many( :children )
should validate_uniqueness_of( :email )
should_not allow_value("blah").for(:email)
should_not allow_value("b lah").for(:email)
should allow_value("a#b.com").for(:email)
should allow_value("asdf#asdf.com").for(:email)
end
When I generated the user model, it created a fixture file. My understanding is that when I run rake, Rails uses that fixture file to generate objects used in the tests. Which is not what I want. I want Rails to use machinist's blueprints just a seamlessly as it uses the fixtures file.
Is there a way to do this? Is there some way to tell rails that it needs to use blueprints instead of fixtures?
Add this to config/application.rb:
config.generators do |g|
g.fixture_replacement :machinist
end
You can safely trash the old fixtures folder too, unless you want to keep them obviously!