Currently working as an engineer in the first year of new graduates. I had little experience in engineering, and I was wondering if I could beat my seniors by taking advantage of my inexperience.
Since I am inexperienced, I have no habit in the mounting procedure. Because I don't have a habit, I decided to try to implement it using TDD in order to get a good habit (maybe there was a different way).
Try TDD development with Ruby on Rails Tutorial. sample_app
[Test Driven Development](https://www.amazon.co.jp/%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95% Take a quick look at E9% 96% 8B% E7% 99% BA-Kent-Beck / dp / 4274217884)
[Test Driven Development](https://www.amazon.co.jp/%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95% Take a look at the video of the author of E9% 96% 8B% E7% 99% BA-Kent-Beck / dp / 4274217884). Clean code that works - How can we go there? - Takuto Wada | SeleniumConf Tokyo
Browse materials for in-house TDD study sessions
Test-driven development. Before implementing, write a test that only passes if the implementation is successful.
Write a test (before implementation) ↓ Test fails (because I haven't implemented anything) ↓ Implement ↓ Tests succeed (because the implementation allows you to pass the tests you wrote before implementation) ↓ Perform refactoring. If the test is successful, then we can say that we were able to fix the code without losing functionality. On the contrary, if the test fails due to refactoring, the function that should have been implemented has been lost, so debug it.
Benefits of TDD
Disadvantages of TDD
In order to suppress the disadvantages of TDD and take advantage of it, it is necessary to have an index of when to TDD.
In short, it's okay to try basic TDD, but if you don't know how to write a test first, you shouldn't do TDD.
Here is a concrete example of a TDD test. The explanation uses various test types (model, controller, integration, etc.), so if you don't know about the test types, please take a look here. Rails test directory structure and test process
Success when requested or verification of the existence of required elements (simple controller test). It's a good idea to write a simple confirmation test first
controller_test.rb
#Controller test
test "should get home" do
get root_path
assert_response :success is #Does the request succeed?
assert_select "title", "HOME TITLE" #Is the title of the page HOME TITLE?
end
First write a test that will succeed if validation is successful. (Example: model test, integration test to check the result of form transmission)
user_test.rb
#User model testing
#Write the following test before describing the validation content in the user model
def setup
@user = User.new(name: "Example User", email: "[email protected]",
password: "hogehoge", password_confirmation: "hogehoge")
end
test "should be valid" do
assert @user.valid?
end
test "name should be present" do
@user.name = " "
assert_not @user.valid?
end
test "email should be present" do
@user.email = " "
assert_not @user.valid?
end
users_signup_test.rb
#New registration test(integration_test)I do
#Tests that you want to fail when trying to register with parameters that are invalid
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user@invalid",
password: "foo",
password_confirmation: "bar" } }
end
end
There is no particular error statement, but write the test first when it is not working as you expected. Write a test that passes only when it works the way you want it, and then make changes to the code to make it work the way you want it.
The logout process will be described as an example. I referred to Ruby on Rails Tutorial 9.14 (two unobtrusive bugs).
Login_controller.rb
class LoginController < ApplicationController
.
.
def destroy #Action to log out
log_out
redirect_to root_url
end
private #From now on, the definition of the function used in the action
#Destroy a persistent session
def forget(user)
user.forget #Empty the user's login memory token stored in the DB
cookies.delete(:user_id) #Empty the contents of cookies
cookies.delete(:remember_token)
end
#Log out the current user
#Current indicating the current user_Empty the user's memory token and session,Variable current_Empty the contents of user
def log_out
forget(current_user)
session.delete(:user_id)
current_user = nil
end
end
In the state of this code, Open two tabs in your browser and in each tab Suppose you are logged in as the same user.
First, log out in one tab. Then the value of current_user becomes nil. After that, when I try to log out on the other tab, current_user is nil, so it becomes nil.forget and an error occurs.
In such a situation, it can be said that "there is no particular error statement, but it is not working as I expected", so I do TDD. Write a test first to see if it works properly when you log out twice.
users_login_test.rb
def setup #Define a user instance to log in to
@user = User.new(name: "Example User", email: "[email protected]",
password: "hogehoge", password_confirmation: "hogehoge")
end
test "two times logout after login" do
log_in(@user)
delete logout_path
assert_not is_logged_in?
assert_redirected_to root_url
#Simulate logging out in another tab
delete logout_path
follow_redirect! #When redirected, check if the page is the one before login
assert_select "a[href=?]", login_path #Check if there is a path for the login page
assert_select "a[href=?]", logout_path, count: 0
assert_select "a[href=?]", user_path(@user), count: 0
end
Make functional changes to pass this test. Finally, add the following code and the test will pass.
Login_controller.rb
class LoginController < ApplicationController
.
.
def destroy #Action to log out
log_out if logged_in? #Use logout function only when logged in
redirect_to root_url
end
end
[Test Driven Development](https://www.amazon.co.jp/%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95%E9 I learned the tips to use when implementing TDD from the book% 96% 8B% E7% 99% BA-Kent-Beck / dp / 4274217884).
In this book, it was written that at least test creation → temporary implementation → triangulation → refactoring should be done.
This time, I will give an example of TDD for creating a function that returns the input argument (int) as a character string.
Create a test for a function (not yet created). At the time of creation, the test will be red.
test "Pattern1 of test returnString function " do
a = returnString(1)
assert_equal a, "1"
end
Define a function that will pass the test written above in any form. The test turns green in the function definition.
def returnString(int)
return "1"
end
Create multiple tests of the same type (two this time) and rewrite the implementation to a general form so that the Rera test passes.
When I create this test, it becomes red. Because the current implementation only returns "1".
test "Pattern2 of test returnString function " do
b = returnString(2)
assert_equal b, "2"
end
Change from a function that returns a raw value of 1 to a function that returns the value received as an argument as a string. The test will be green.
def returnString(int)
return "#{int}"
end
Even if you do TDD for the minimum implementation as described above, the code will become complicated as more functions you want to add. but it's okay. Thanks to writing the test, you can judge the function of the code accurately. Therefore, you can refactor with confidence.
By doing TDD, you can think about what you want to implement first. I felt that it made the implementation efficient. I also felt that writing tests would make it easier to maintain code maintainability because I could refactor with confidence.
Not limited to TDD, when writing a test, you do not have to try to write multiple types of test contents at once. If you try to write at once and the test content itself is wrong, you will fall over
It is not good to overwrite the contents of the test as comprehensively as the above contents. If you write too comprehensively (such as thoroughly checking the existence of HTML elements), if there is a change in the function in the future, it will take more time to maintain and modify the test itself.
Not limited to TDD, when writing validation tests, you have to write both tests when you want it to work and tests when you don't want it to work.
users_signup_test.rb
#Test new registration
#Tests that you want to fail when trying to register with parameters that are invalid
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user@invalid",
password: "foo",
password_confirmation: "bar" } }
end
end
#Tests that you want to succeed when trying to register with valid parameters
test "valid signup information" do
get signup_path
assert_difference 'User.count', 1 do
post users_path, params: { user: { name: "Example User",
email: "[email protected]",
password: "password",
password_confirmation: "password" } }
end
end
controller
def create
if (user_id = session[:user_id])
.
.
else
raise #If the test passes, you know that this part has not been tested → Write the test so that this part is tested
.
.
end
end
Ruby on Rails Tutorial [Test Driven Development](https://www.amazon.co.jp/%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95%E9 % 96% 8B% E7% 99% BA-Kent-Beck / dp / 4274217884) Clean code that works - How can we go there? - Takuto Wada | SeleniumConf Tokyo
Recommended Posts