Regarding the test framework of the web application currently being created, we are migrating from Minitest to RSpec in consideration of Last created article. At that time, I felt that it was necessary to be aware of the types of tests that Minitest was not very aware of. How to write a test depends on whether the test is a UI test, a controller test, or a model test. This article briefly summarizes RSpec's directory structure and how to write code for each test type. I referred to it → Introduction to RSpec that can be used, Part 1 "Understanding the basic syntax and useful functions of RSpec" Everyday Rails-Introduction to Rails Testing with RSpec
The type of RSpec test is called Spec. Specs are usually placed in a canonical directory structure that describes their purpose.
** [Main RSpec directory structure] **
spec --- models | | ー requests | | ー features | | ー helpers | | ー mailers | | ー system
Here, we will introduce the roles of ** Model Spec **, ** Request Spec **, ** Feature Spec **, and ** System Spec **.
Model Spec Model Spec tests Rails models such as validation. Check with the sample code below.
spec/models/user_spec.rb
RSpec.describe User, type: :model do
describe 'username' do
it 'If blank, it is in an invalid state' do
user = User.new( name: ' ',
email: '[email protected]',
password: 'password',
password_confirmation: 'password')
expect(user).to_not be_valid
end
it 'If it is too long, it is in an invalid state' do
user = User.new( name: 'a' * 51,
email: '[email protected]',
password: 'password',
password_confirmation: 'password')
expect(user).to_not be_valid
end
end
end
I will check the contents one by one.
-Specify spec with type:: model
. We are testing the user model here.
· describe
groups the expected results.
It declares "testing the user model" and "testing the user name" in it.
-`ʻUser.new`` creates a user.
-ʻit`` is grouped in units smaller than `` describe`` called ** example **. You can have multiple expectations within
ʻit. However, if it fails in the middle, it will not flow beyond that, so basically one expectation for each `ʻit
is recommended.
・ ʻExpect`` describes the expectation (expectation).
ʻexpect (user) .to_not be_validis testing that" user is not valid? " One of the advantages of RSpec is that you can write tests like in English. The
to_not be_valid`` part is called ** matcher ** and there are various types.
Other matchers can be found at here.
Request Spec The Request Spec is according to the Official Documentation (https://relishapp.com/rspec/rspec-rails/v/4-0/docs/request-specs/request-spec) "via the full stack including routing". , Designed to drive operation without stubs [^ 1]. " The point is server-side testing. Request Spec allows you to use HTTP methods (get, post, delete, etc.) when testing the response of the controller.
spec/requests/access_to_users_spec.rb
RSpec.describe "AccessToUsers", type: :request do
describe 'Access restrictions for users who are not logged in' do
it 'Delete user' do
user = User.create( name: 'kojiro',
email: '[email protected]',
password: 'password',
password_confirmation: 'password')
expect {
delete user_path(user)
}.to change(User, :count).by(0)
end
end
end
Here we use the delete
method to test that a non-logged-in user deletes a user but it is not reflected.
```expect {delete user_path (user)} .to change (User,: count) .by (0) `` means that when accessing delete user_path (user), the difference in the number of users before and after access is 0. Predict. "
Feature Spec Feature Spec is a UI test. Official documentation recommends using System Spec, which has similar functionality from RSpec 3.7. doing.
System Spec System Specs, like Feature Specs, are primarily UI tests. You can write an E2E (end-to-end) test to run on your browser. This is a front test. Also, by default, we are running tests using the Chrome browser. Use ** Capybara ** to simulate browser activity. We will check what kind of description it will be with the sample code below.
spec/systems/user_logins_spec.rb
RSpec.describe "UsersLogins", type: :system do
it 'Valid login' do
user = User.create( name: 'kojiro',
email: '[email protected]',
password: 'password',
password_confirmation: 'password')
visit login_path
fill_in 'mail address', with: '[email protected]'
fill_in 'password', with: 'password'
click_button 'Login'
expect(current_path).to eq user_path(user)
end
end
I will check the contents one by one.
-First, create a user with `ʻUser.create`` and save it.
-Methods such as visit
, fill_in
, and click_button
are Capybara's domain-specific languages called DSL.
visit
represents a visit to the page. Use visit login_path
to go to the login page. fill_in
represents the input of characters in the text box. In fill_in'email address', with:'[email protected]'
, you have entered'[email protected]' in the text box named'email address'. click_button
Represents a button click.Other DSLs are listed here [https://github.com/teamcapybara/capybara#the-dsl).
· `ʻExpect (current_path) .to eq user_path (user) `` tests" whether the current path is the same as user_path (user) (user details) ".
In addition, when the test fails, the System spec displays a screenshot of the failed part along with an error message of the failed content.
As a test, change the ```user_path (user) of the expectation to
login_path`` and run the test.
Then the following output was output.
Failures:
1)UsersLogins Valid login, logout
Failure/Error: expect(current_path).to eq login_path
expected: "/login"
got: "/users/1"
(compared using ==)
[Screenshot]: /(Program path)/myapp/tmp/screenshots/failures_r_spec_example_groups_users_logins_Valid login, logout_119.png
# ./spec/system/users_login_spec.rb:14:in `block (2 levels) in <top (required)>'
As you can see in the screenshot, you are seeing the ʻuser_path (user)
`(user details screen) instead of the login_path
(login screen) you expected the results to be.
By the way, in Request Spec, Capybara cannot be used, and in Feature Spec, HTTP method cannot be used. It will be necessary to determine whether the test you are about to implement is server-side or front-side.
[^ 1]: A stub is a substitute for a submodule that a module calls when testing a module in a computer program. ([wiki](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%BF%E3%83%96#:~:text=%E3%82%B9%E3% 82% BF% E3% 83% 96% EF% BC% 88stub% EF% BC% 89% E3% 81% A8% E3% 81% AF% E3% 80% 81,% E7% 94% A8% E3% 81 % 84% E3% 82% 8B% E4% BB% A3% E7% 94% A8% E5% 93% 81% E3% 81% AE% E3% 81% 93% E3% 81% A8% E3% 80% 82 & text =% E9% 80% 86% E3% 81% AB% E4% B8% 8A% E4% BD% 8D% E3% 83% A2% E3% 82% B8% E3% 83% A5% E3% 83% BC% E3% 83% AB% E3% 81% AE,% E3% 82% A6% E3% 82% A7% E3% 82% A2% E3% 81% AE% E5% A0% B4% E5% 90% 88% EF From% BC% 89% E3% 81% A8% E5% 91% BC% E3% 81% B6% E3% 80% 82)) Here, Rails works in the test without using a substitute for its lower module Is able to drive.