SOFTWARE DEVELOPMENT | 3 mins read

TDD with Ruby on Rails

By Pangpond on 02 Sep 2020
sennalabs-blog-banner

TDD ย่อมากจาก Test-Driven Development และหลักการของ TDD คือการเริ่มจากการเขียน Test ก่อนที่จะเขียน Code เพื่อให้ Test ผ่าน จากนั้น ก็ทำการ Refactor Code เพื่อให้อ่าน และเข้าใจง่ายยิ่งขึ้น ตามภาพข้างล่าง

 

 

  • จากการรัน Test รอบแรก Test จะพังทั้งหมด (RED)
  • จากนั้นเขียนโค้ดเพื่อให้ทดสอบผ่าน (GREEN)
  • และ Refactor Code ให้ผ่านง่ายขึ้น (REFACTOR)
  • แล้วก็ทำซ้ำๆๆ จนทดสอบผ่านทั้งหมด

 

ในบทความนี้เราจะมาทำ TDD ด้วย Rails On Rails และ Minitest ที่มากับ Rails มาลุยกันเลย

1.  เริ่มจากการสร้างแอปฯ​ ก่อน

$ rails new cats --skip-javascript

 

2. สร้าง Cat Model

$ rails generate model cat name

 

3. run migration

$ rails db:migrate

 

4. สร้าง Controller เพื่อจะทำรายการของแมว

$ rails g controller cats index

 

5. เขียน Test

# test/fixtures/cats.yml
                one:
                  name: British Shorthair
                
                two:
                  name: Scottish fold
                
# test/controllers/cats_controller_test.rb
                require 'test_helper'
                
                class CatsControllerTest < ActionDispatch::IntegrationTest
                  test "should get index" do
                    get cats_url
                    assert_response :success
                  end
                
                  test "should see cat list" do
                    get cats_url
                    assert_match "British Shorthair", @response.body
                    assert_match "Scottish fold", @response.body
                  end
                
                  test "should create cat" do
                    post cats_url, params: {
                      cat: {name: "American Shorthair"}
                    }
                    assert_redirected_to cats_url
                    assert_match "American Shorthair", @response.body
                  end
                end
                

 

6. มาลอง run test กัน

$ rails test
                 rails test
                Running via Spring preloader in process 93131
                Run options: --seed 30862
                
                # Running:
                
                E
                
                Error:
                CatsControllerTest#test_should_get_index:
                NameError: undefined local variable or method `cats_url' for #<CatsControllerTest:0x00007fbe91a54400>
                    test/controllers/cats_controller_test.rb:5:in `block in <class:CatsControllerTest>'
                
                
                rails test test/controllers/cats_controller_test.rb:4
                
                E
                
                Error:
                CatsControllerTest#test_should_see_cat_list:
                NameError: undefined local variable or method `cats_url' for #<CatsControllerTest:0x00007fbe91a54400>
                    test/controllers/cats_controller_test.rb:10:in `block in <class:CatsControllerTest>'
                
                
                rails test test/controllers/cats_controller_test.rb:9
                
                
                
                Finished in 0.173663s, 11.5166 runs/s, 0.0000 assertions/s.
                2 runs, 0 assertions, 0 failures, 2 errors, 0 skips

แล้ว Test ก็พังตามที่เราคิดไว้ จากนั้นเราก็มาเขียน Code เพื่อ ทำให้ Test ผ่าน

 

7. จาก  "undefined local variable or method `cats_url' คือไม่มี path เราต้องไปแก้ที่ `config/routes.rb`

Rails.application.routes.draw do
                  resources :cats
                end

 

8. run test

$ rails test
                Running via Spring preloader in process 93217
                Run options: --seed 65184
                
                # Running:
                
                .F
                
                Failure:
                CatsControllerTest#test_should_see_cat_list [/Users/theeraphatjantakat/sennalabs/cats/test/controllers/cats_controller_test.rb:11]:
                Expected /British\ Shorthair/ to match "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Cats</title>\n    \n    \n\n    <link rel=\"stylesheet\" media=\"all\" href=\"/assets/application-04c3ae28f07e1bf734223bf526d0cdd296440ef53bcb3f80b9f093c6bf02f747.css\" />\n  </head>\n\n  <body>\n    <h1>Cats#index</h1>\n<p>Find me in app/views/cats/index.html.erb</p>\n\n  </body>\n</html>\n".
                
                
                rails test test/controllers/cats_controller_test.rb:9
                
                
                
                Finished in 0.284871s, 7.0207 runs/s, 10.5311 assertions/s.
                2 runs, 3 assertions, 1 failures, 0 errors, 0 skips

 

9. แล้วก็เขียนโค้ด

# app/controllers/cats_controller.rb
                class CatsController < ApplicationController
                  def index
                    @cats = Cat.all
                  end
                end
                
# app/views/cats/index.html.erb
                <% @cats.each do |cat| %>
                  <%= cat.name %>
                <% end %>
                

 

10.  แล้วก็ run test

rails test
                Running via Spring preloader in process 93423
                Run options: --seed 52351
                
                # Running:
                
                ..
                
                Finished in 0.193870s, 10.3162 runs/s, 25.7905 assertions/s.
                2 runs, 5 assertions, 0 failures, 0 errors, 0 skips

 

และแล้ว Test ทั้งหมดก็ผ่าน และสังเกตได้ว่า ตั้งแต่ข้อ 5 - 10 คือการทำ TDD (RED -> GREEN -> REFACTOR) เนื่องจาก Code ชุดนี้ค่อนข้างเล็กและเข้าใจง่ายทำให้ไม่ต้อง Refactor Code

 

จบไปแล้วกับการทำ TDD ซึ่งการที่เราทำ TDD จะทำให้เราเขียน develop งานได้ตรงตาม requirement ของลูกค้าเนื่องจากเราเขียน Test อิงตาม use case ต่างๆ จาก requirement และการทำ TDD จะทำให้มี Code Coverage เพิ่มขึ้นอีกด้วย แต่ในทางกลับกันถ้าเราเข้าใจ requirement หรือ used case ผิดแล้วมาเขียน Test ก็ให้ code ที่เราเขียนผิดได้เช่นกัน

Written By