Airborne GET from test environment - ruby-on-rails

Using Airborne, how can I use GET to return values from test environment?
For example, I have the following test:
it 'should foo the bar' do
product = FactoryGirl.create(:product)
get "/v1/products"
pp product
pp body
expect_status 200
end
The output of product is:
#<Product:0x007f9dfe601078
id: 847,
ref: "038-71-8140174268593",
name: "Adipisci Sqryujdcoefpthnzbvagwlxikm",
description:
"Lorem ipsum...",
created_at: Tue, 01 Nov 2016 15:48:25 UTC +00:00,
updated_at: Tue, 01 Nov 2016 15:48:25 UTC +00:00>
But the output of body is:
"{\"data\":[]}"
I have rails server running test environment with rails s -e test
If I run rails server with development environment, the body returns all products from dev environment with no problems.

I was running the test with RSpec.describe ProductResource, type: :resource do instead of RSpec.describe 'Products', type: :request do.

Related

Rails 5: How do I point fixtures to other fixtures?

I three models Comment, User and Project. Project and Comment need to point to other objects in order to be valid. For example, a comment needs to point to an author (user) and a project.
The associated fixture files look like this:
# comments.yml
test_comment:
author: users(:test_user)
project: projects(:test_project)
# users.yml
test_user:
name: 'test user'
# projects.yml
test_project:
name: 'Test'
description: 'This is a test'
owner: users(:test_user)
However, I've found that my fixtures are probably set up incorrectly. Rails returns false if I try to save the comment:
assert_equal true, comments(:test_comment)
#=> false
I can see that there are foreign keys for a project and author:
=> #<Comment:0x00007f9b1661f3d8
id: 137725605,
body: "",
project_id: 745075726,
author_id: "31ceee04-5307-5059-91db-0dc2068a780c",
created_at: Fri, 22 Feb 2019 13:17:58 UTC +00:00,
updated_at: Fri, 22 Feb 2019 13:17:58 UTC +00:00>
But when I interrogate them, Rails returns nil.
> comments(:test_comment).author
=> nil
> comments(:test_comment).project
=> nil
I expected that one would return users(:test_user) and the other would return projects(:test_project). I thought perhaps I needed to use ERB in my yaml:
test_comment:
author: <%= users(:test_user) %>
project: <%= projects(:test_project) %>
But that results is a stream of errors when I run my tests:
NoMethodError: undefined method `users' for #<#<Class:0x00007f9b17692ff8>:0x00007f9b17692dc8>
What do I need to do to point fixtures to other fixtures? Can it be done? What have I done wrong?
In the Rails guide on Testing with YAML fixtures, you can see that you don't need users(:test_user) to refer to some other object. Instead, you can simply write test_user:
# comments.yml
test_comment:
author: test_user
project: test_project
# users.yml
test_user:
name: 'test user'
# projects.yml
test_project:
name: 'Test'
description: 'This is a test'
owner: test_user
Hope this helps!

is it possible to override built-in Ruby methods?

I am working on a problem where I have to pass an rpsec test. The problem is that the method is using the same name as a built in ruby method .count
given that I cannot change the rspec test, is it possible to override .count to behave differently? if not, is there a better way to get around this?
here is the rspec test I am trying to pass
subject = FinancialSummary.one_day(user: user, currency: :usd)
expect(subject.count(:deposit)).to eq(2)
my code:
class FinancialSummary
def self.one_day(user: user, currency: currency)
one_day_range = Date.today.beginning_of_day..Date.today.end_of_day
find_transaction(user.id, currency).where(created_at: one_day_range)
end
def self.find_transaction(user_id, currency)
Transaction.where(user_id: user_id,
amount_currency: currency.to_s.upcase
)
end
end
output:
[#<Transaction:0x00007f9b39c2e9b8
id: 1,
user_id: 1,
amount_cents: 1,
amount_currency: "USD",
category: "deposit",
created_at: Sat, 10 Mar 2018 18:46:53 UTC +00:00,
updated_at: Sat, 10 Mar 2018 18:46:53 UTC +00:00>,
#<Transaction:0x00007f9b3d0dbc38
id: 2,
user_id: 1,
amount_cents: 2000,
amount_currency: "USD",
category: "deposit",
created_at: Sat, 10 Mar 2018 18:47:43 UTC +00:00,
updated_at: Sat, 10 Mar 2018 18:47:43 UTC +00:00>,
#<Transaction:0x00007f9b3d0b3fa8
id: 7,
user_id: 1,
amount_cents: 1200,
amount_currency: "USD",
category: "withdraw",
created_at: Mon, 05 Mar 2018 02:22:42 UTC +00:00,
updated_at: Tue, 06 Mar 2018 18:48:20 UTC +00:00>]
it is printing out, what I believe to be the correct information, up until the test attempts to count the transactions by their category: 'deposit'. Then I get this error message:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: deposit: SELECT COUNT(deposit) FROM "transactions" WHERE "transactions"."user_id" = ? AND "transactions"."amount_currency" = ?
EDITED FOR MORE INFO
Some Assumptions Were Made in the Writing of this answer and modifications may be made based on updated specifications
Overriding count is a bad idea because others who view or use your code will have no idea that this is not the count they know and understand.
Instead consider creating a scope for this like
class FinancialSummary < ApplicationRecord
scope :one_day, ->(user:,currency:) { where(user: user, currency: currency) } #clearly already a scope
scope :transaction_type, ->(transaction_type:) { where(category: transaction_type) }
end
then the test becomes
subject = FinancialSummary.one_day(user: user, currency: :usd)
expect(subject.transaction_type(:deposit).count).to eq(2)
SQL now becomes:
SELECT COUNT(*)
FROM
"transactions"
WHERE
"transactions"."user_id" = ?
AND "transactions"."amount_currency" = "usd"
AND "transactions"."category" = "deposit"
Still very understandable and easy to read without the need to destroy the count method we clearly just used.
It's not clear what object the count message is being sent to because I don't know what FinancialSummary.one_day(user: user, currency: :usd) returns, but it seems like you are saying count is a method on whatever it returns, that you can't change. What does FinancialSummary.one_day(user: user, currency: :usd).class return?
Perhaps one solution would be to alias it on that object by adding alias_method :count, :account_count and then in your test calling expect(subject.account_count(:deposit)).to eq(2)
It would be easier if you could post the FinancialSummary#one_day method in your question.

How to correctly use Devise + Confirmable + fixtures

I'm using Devise + Confirmable for user authentication and Minitest + Capybara + fixtures for testing. I can make working logged in tests for users as long as I included some form of login (either with the helper login_as(#user) or going to the login page with Capybara) and the line#user.confirm before running.
How can I confirm users in the fixture itself though so I don't have to include this line every time.
Right now I have:
confirmed_at: Time.now
in the yml, but it doesn't seem to make a difference.
Here is a working sample test, if it's useful for illustration:
def setup
#user = users(:user)
end
test 'user should be redirected to profile edit on first login' do
#user.confirm
visit(new_user_session_path)
fill_in('user_email', :with => #user.email)
fill_in('user_password', :with => 'foobar')
click_button('Log in')
assert_current_path(edit_user_registration_path)
end
and the user fixture:
user:
email: test1#example.com
confirmed_at: Time.now
encrypted_password: <%= Devise::Encryptor.digest(User, 'foobar') %>
sign_in_count: 0
I updated my answer. The solution of the problem is found here. You need to configure it as in the guide and call the method user.confirm! inside the module ControllerMacros method def login_user
https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-(and-RSpec)#controller-specs
Open source github page of a complete Devise Rspec Project including testing
https://github.com/RailsApps/rails-devise/blob/master/spec/features/users/sign_in_spec.rb
you are trying to set
User.confirmed_at = Time.now
but confirmed_at has datetime datatype at least in my postegresql db, so this may depend on the db you are using.
confirmed_at: datetime
So this is the Time.now format
pry(main)> Time.now
=> 2017-03-14 11:14:06 +0100
While this is is the User.confirmed_at format
user.confirmed_at
=> Sun, 05 Mar 2017 15:05:03 UTC +00:00
So you should use a compatible format/variable, try to search the DateTime class for a compatible format that includes the UTC as DateTime.now returns:
[40] pry(main)> DateTime.now
=> Tue, 14 Mar 2017 11:19:25 +0100
DateTime has a utc() method. If I run this it is almost what is needed.
DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc
DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc
=> 2005-02-21 16:11:12 UTC
Check the DateTime api and if needed you can check the utc() method on github
DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600
DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 UTC
# File activesupport/lib/active_support/core_ext/date_time/calculations.rb, line 168
def utc
utc = new_offset(0)
Time.utc(
utc.year, utc.month, utc.day,
utc.hour, utc.min, utc.sec + utc.sec_fraction
)
end
http://api.rubyonrails.org/classes/DateTime.html#method-i-utc

in rspec, why do I need the '[]' in expect().to eq([])?

Rails app, writing specs:
RSpec.describe AdvertisementsController, :type => :controller do
let(:my_ad) { Advertisement.create!(title: 'title', copy: 'copy text', price: 10)}
describe 'GET #index' do
...
...
it 'renders my_ad' do
get :index, {id: my_ad.id}
expect(assigns[:advertisements]).to eq(my_ad)
end
end
...
...
end
I wrote the above, which gave the error below.
1) AdvertisementsController GET #index renders my_ad
Failure/Error: expect(assigns[:advertisements]).to eq(my_ad)
expected: #<Advertisement id: 1, title: "title", copy: "copy text", price: 10, created_at: "2016-02-26 02:39:20", updated_at: "2016-02-26 02:39:20">
got: #<ActiveRecord::Relation [#<Advertisement id: 1, title: "title", copy: "copy text", price: 10, created_at: "2016-02-26 02:39:20", updated_at: "2016-02-26 02:39:20">]>
(compared using ==)
Diff:
## -1,8 +1,8 ##
-#<Advertisement:0x007ff6995e19f0
- id: 1,
- title: "title",
- copy: "copy text",
- price: 10,
- created_at: Fri, 26 Feb 2016 02:39:20 UTC +00:00,
- updated_at: Fri, 26 Feb 2016 02:39:20 UTC +00:00>
+[#<Advertisement:0x007ff6994f9560
+ id: 1,
+ title: "title",
+ copy: "copy text",
+ price: 10,
+ created_at: Fri, 26 Feb 2016 02:39:20 UTC +00:00,
+ updated_at: Fri, 26 Feb 2016 02:39:20 UTC +00:00>]
With this code, the test will pass. All it seems to do is add [..] around the variable, as such (| & ^ to emphasize location):
Why does that work?
RSpec.describe AdvertisementsController, :type => :controller do
let(:my_ad) { Advertisement.create!(title: 'title', copy: 'copy text', price: 10)}
describe 'GET #index' do
...
...
it 'renders my_ad' do
get :index, {id: my_ad.id}
expect(assigns[:advertisements]).to eq([my_ad])
end ^ ^
end | |
...
...
end
of note I can see that the objects have different identifying id's, so I think that's part of the reason and the [..]'s either ignore the mismatch or something, but I'd like to understand it.
-#<Advertisement:0x007ff6995e19f0 vs . +[#<Advertisement:0x007ff6994f9560
[] is Ruby syntax for an array. So your test is expecting to assign an array of advertisements (or in this case, something that behaves like an array, such as an ActiveRecord::Relation), containing just one element, my_ad.
Your code also sounds quite strange in that you're providing an ID to the index action, expecting only that record to be returned. Index actions are for listing out groups of records - a show action is for showing the details of a single record.

faker and factory_girl troubles [duplicate]

This question already has answers here:
Faker is producing duplicate data when used in factory_girl
(4 answers)
Closed 7 years ago.
Good day!
I'm trying to create random values when creating model in my tests. Using factory_girl and faker gems
as of my gemfile.lock
factory_girl_rails (4.5.0)
factory_girl (~> 4.5.0)
faker (1.6.1)
I define factory as follows
FactoryGirl.define do
factory :action do
name Faker::Internet.email
id Faker::Number.digit
ip Faker::Internet.ip_v4_address
old_value Faker::Number.number(7)
end
end
but in my tests when i try to create objects, they have the same attributes.
> create :logger
id: 17,
new_value: 3133860,
name: "fidel_murazik#gibson.biz",
ip: "118.247.64.189",
created_at: Tue, 02 Feb 2016 09:12:50 UTC +00:00,
updated_at: Tue, 02 Feb 2016 09:12:50 UTC +00:00>
and the second time
> create :logger
id: 18,
new_value: 3133860,
name: "fidel_murazik#gibson.biz",
ip: "118.247.64.189",
created_at: Tue, 02 Feb 2016 09:12:53 UTC +00:00,
updated_at: Tue, 02 Feb 2016 09:12:53 UTC +00:00>
One more time - I'm trying to get via Faker gem random valid attributes for factory_girl every time I create a new object.
Can you please help me with this problem?
You may use sequence to generate different data every time running:
FactoryGirl.define do
factory :action do
sequence(:name) { |n| "user_#{n}#factory.com" }
sequence(:id) { |n| Faker::Number.digit + n }
ip Faker::Internet.ip_v4_address
old_value Faker::Number.number(7)
end
end
Btw id will be generated automatically, feel free to remove id!

Resources