I will fix it from the factory.
The one that is flying the most
ActiveRecord::RecordInvalid:
Validation failed: User must exist
That's the error.
This is caused by the user_id becoming nil when create (: post)
is done.
Let's crush it by modifying the factory.
spec/factories/posts.rb
factory :post do
subject { "MyString" }
body { "MyText" }
+
+ after(:build) do |obj|
+ obj.user = build(:user) if obj.user.nil?
+ end
end
ʻAfter (: build)` is executed after build or create. You can eliminate the User must exist error by putting the built user in post.user.
In addition, by doing ʻif obj.user.nil?, When a specific user is passed and created like
create (: post, user: user)`, it is prevented from being overwritten by internal processing. I will.
Actually, in a simpler way, most of them can be crushed for the time being.
spec/factories/posts.rb
factory :post do
subject { "MyString" }
body { "MyText" }
+ user
end
However, this method is good when create (: post)
is done, but since user returns with nil when build (: post)
is done, the former is supported.
spec/requests/v1/posts_request_spec.rb
describe "POST /v1/posts#create" do
+ let(:authorized_headers) do
+ user = create(:user)
+ post v1_user_session_url, params: { email: user.email, password: "password" }
+ headers = {}
+ headers["access-token"] = response.header["access-token"]
+ headers["client"] = response.header["client"]
+ headers["uid"] = response.header["uid"]
+ headers
+ end
let(:new_post) do
attributes_for(:post, subject: "create_subject test", body: "create_body test")
end
it "Normal response code is returned" do
- post v1_posts_url, params: new_post
+ post v1_posts_url, params: new_post, headers: authorized_headers
expect(response.status).to eq 200
end
it "One more case will be returned" do
expect do
- post v1_posts_url, params: new_post
+ post v1_posts_url, params: new_post, headers: authorized_headers
end.to change { Post.count }.by(1)
end
it "subject,body returns correctly" do
- post v1_posts_url, params: new_post
+ post v1_posts_url, params: new_post, headers: authorized_headers
json = JSON.parse(response.body)
expect(json["post"]["subject"]).to eq("create_subject test")
expect(json["post"]["body"]).to eq("create_body test")
end
it "Errors are returned when the parameter is invalid" do
- post v1_posts_url, params: {}
+ post v1_posts_url, params: {}, headers: authorized_headers
json = JSON.parse(response.body)
expect(json.key?("errors")).to be true
end
end
Generate a user and log in based on that user information.
By adding the 3 keys for authentication in the response header to headers and posting, access is performed while being authenticated as a create (: user)
user.
However, the error remains because the controller side has not been fixed yet.
app/controllers/v1/posts_controller.rb
def create
- post = Post.new(post_params)
+ post = current_v1_user.posts.new(post_params)
if post.save
The above fix should pass the test.
To explain the behavior, first of all, since the authentication information is passed by headers, the method called current_v1_user
can be used in controller. This will return the logged-in user instance.
In other words, current_v1_user.posts.new
instantiates the post associated with the logged-in user.
This will create a post for the logged in user.
The test has passed, but when implementing authorization with Pundit in the future, if you write the process to get the authenticated header each time, maintainability will decrease, so move to the helper for spec I will.
The helper for spec is generally placed in spec / support, so create a directory.
$ mkdir spec/support
$ touch spec/support/authorization_spec_helper.rb
I will bring the processing that was suitable for rspec to this place.
spec/support/authorization_spec_helper.rb
# frozen_string_literal: true
#
#Authentication helper
#
module AuthorizationSpecHelper
def authorized_user_headers
user = create(:user)
post v1_user_session_url, params: { email: user.email, password: "password" }
headers = {}
headers["access-token"] = response.header["access-token"]
headers["client"] = response.header["client"]
headers["uid"] = response.header["uid"]
headers
end
end
If you just place it under spec / support, it will not be read by itself, so modify spec / rails_helper.rb.
spec/rails_helper.rb
-# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
+Dir[Rails.root.join("spec", "support", "**", "*.rb")].sort.each { |f| require f }
...
RSpec.configure do |config|
...
+ config.include(AuthorizationSpecHelper, type: :request)
end
Enable the process of reading under spec / support that was commented out, and include AuthorizationSpecHelper. By writing as above, only the request spec will be valid.
spec/requests/v1/posts_request_spec.rb
...
require "rails_helper"
RSpec.describe "V1::Posts", type: :request do
+ let(:authorized_headers) do
+ authorized_user_headers
+ end
...
describe "POST /v1/posts#create" do
- let(:authorized_headers) do
- user = create(:user)
- post v1_user_session_url, params: { email: user.email, password: "password" }
- headers = {}
- headers["access-token"] = response.header["access-token"]
- headers["client"] = response.header["client"]
- headers["uid"] = response.header["uid"]
- headers
- end
...
The rest is completed with the above correspondence. If the test result remains green, it's OK for the time being.
All the tests pass, but the test code is inadequate in the first place. If you hit #create when you are not authenticated, you will get a 500 error, and the current specifications that you can update or delete posts other than yourself are troublesome, so we will finally add authorization next time.
Next time, we will maintain the seed. That's all for today.
→ Building a bulletin board API with authentication authorization in Rails 6 # 14 Seeing execution time display [To the serial table of contents]