Test Driven Development - Basic1
TIL
589
White

lucas2804 viết ngày 26/03/2017

Test Driven Development - Basic1

Lời nói đầu

  • Chào các bạn, cái title ở trên chỉ là giật tít thôi, đừng chém nhé.

  • Vậy TDD là gì: TDD chỉ là 1 hướng lập trình mà các bạn phải viết test trước khi code. Chưa có test mà đã có code thì cái test đó should fail, nên fail, phải fail, không fail thì sai lắm lắm.

  • Cuối cùng là lợi ích thì mình nói sau, thực hành trước nhé.

Triển

  • Vậy nên bắt đầu TDD từ đâu: TDD nên bắt đầu khi các bạn đã rõ ràng requirements của khách hàng, liệt kê ra được hầu hết các trường hợp có thể xảy ra...

1) Lấy requirements

  • Viết 1 application quản lý projects và tasks, tính toán tổng manday của project, số manday còn lại dựa trên tasks. Sơ sơ là vậy. (1 manday là 1 ngày làm việc của engineer)

2) Sau khi lấy requirements thì mình tốn có 3 phút để viết tests à mấy chế.

alt text

# Vậy mình cần 
# rails g rspec:install
# rails g rspec:model Project      -> Tạo file project_spec.rb để test chớ
# rails g model Project                 -> Tạo cái project để test chớ
# rake db:migrate

3) Ok kế hoạch test đã lên, và nó fail rồi mừng vãi, giờ chỉ là viết code cho nó pass

  • Run rspec spec/models/project_spec.rb:5 Finished in 0.04554 seconds
  • 3.1) Rõ ràng là project.tasks là quan hệ 1:n trong database rồi, vậy migrate Task < ActiveRecord để model Project có has_many: tasks <=> project.tasks.
 Failure/Error: project.tasks << complete_task1

     NoMethodError:
       undefined method `tasks' for #<Project id: nil, created_at: nil, updated_at: nil>
     # ./spec/models/project_spec.rb:7:in `block (2 levels) in <top (required)>'

  • 3.1) Xử lý project.tasks
  • Run rspec spec/models/project_spec.rb:5

    # rails g model Task name:string manday:integer project:references
    # rake db:migrate
    def change
    create_table :tasks do |t|
      t.string :name
      t.integer :manday
      t.references :project, index: true
    
      t.timestamps
    end
    end
    
    class Project < ActiveRecord::Base
    has_many :tasks # make project.tasks available
    end
    

alt text

  • Note: https://github.com/lucas2804/TDD-hardcore (check log: " log2 resolved undefine project.task")

  • Run rspec spec/models/project_spec.rb:5 Finished in 0.04554 seconds

  • 3.2) Lồi thêm bug nữa undefined local variable or method 'complete_task1'

  • complete_task1, complete_task2 chưa khởi tạo.

1) Project Projet have all tasks are marked as completed IS DONE
     Failure/Error: project.tasks << complete_task1

     NameError:
       undefined local variable or method `complete_task1' for #<RSpec::ExampleGroups::Project:0x007fa5336110b8>

 it "Projet have all tasks are marked as completed IS DONE" do
    # Setup
    project = Project.new
    complete_task1 = Task.new()
    complete_task2 = Task.new()
    project.tasks << complete_task1
    project.tasks << complete_task2
  end
  • Run rspec spec/models/project_spec.rb:5 Finished in 0.04554 seconds
  • 3.3) Lồi thêm bug nữa undefined methoddone?'`
  1) Project Projet have all tasks are marked as completed IS DONE
     Failure/Error: project.done?.should be_truthy

     NoMethodError:
       undefined method `done?' for #<Project id: nil, created_at: nil, updated_at: nil>

class Project < ActiveRecord::Base
  has_many :tasks

  def done?
    tasks.select(&:completed_at) == tasks.size
  end
end

  • Run rspec spec/models/project_spec.rb:5 Finished in 0.04554 seconds
  • 3.4) Lồi thêm bug nữa undefined method 'completed?'
  • tasks.select(&:completed_at) phân biệt hoàn thành hay không thông qua completed_at giống deleted_at (no problem right?)
  • -> Hình như là thiếu column completed_at đúng không, vậy migrate thêm thì Task sẽ tự động có thuộc tính completed_at ez game ^^.
1) Project Projet have all tasks are marked as completed IS DONE
     Failure/Error: tasks.select(&:completed_at) == tasks.size

     NoMethodError:
       undefined method `completed_at?' for #<Task:0x007fa5387763e0>

# rails g migration add_completed_at_to_tasks
class AddCompletedAtToTasks < ActiveRecord::Migration
  def change
    add_column :tasks, :completed_at, :datetime
  end
end

# rake db:migrate
  • Run rspec spec/models/project_spec.rb:5 Finished in 0.04554 seconds
  • 3.5) Lồi thêm bug nữa Failure/Error: project.done?.should be_truthy
Failure/Error: project.done?.should be_truthy

       expected: truthy value
            got: false
def done?
    tasks.select(&:completed_at) == tasks.size
end
  • Run rspec spec/models/project_spec.rb:5 Finished in 0.04554 seconds
  • 3.6) Ok ca last hit này khá lầy, sao mà ko có cái task nào complete, mình debug coi thử task đang ở trạng thái gì nhé.

alt text

# project.rb
  def done?
    tasks.select(&:completed_at).size == tasks.size
  end

# project_spec.rb
  it "Projet have all tasks are marked as completed IS DONE" do
    # Setup
    project = Project.new
    complete_task1 = Task.new(completed_at: DateTime.now)
    complete_task2 = Task.new(completed_at: DateTime.now)
    project.tasks << complete_task1
    project.tasks << complete_task2

    # Expect
    project.done?.should be_truthy
  end



alt text

4) Chốt lại 1 tí

  • Để cho test pass mình phải run test rồi xem error rồi code cho nó pass.

  • Việc mình nhận feedback từ việc run test tốn tầm 0.04554 seconds thay cho ra browser refresh page lại tốn vài seconds đúng hôm.

  • Viết test sai thì sửa test như việc thêm complete_task1 = Task.new(), các bạn không cần phải viết test đúng 100% ngay từ đầu (Quan trọng ý tưởng phù hợp với requirements của khách hàng).

  • Nếu bạn không viết tests luôn thì sẽ thế nào? bạn sẽ phải check cả 4 requirements ở trên vài lần, vài chục lần trước khi giao cho khách hàng. -> Hay bạn chỉ cần run test vài chục lần và bảo đảm không bỏ sót trường hợp nào, và bảo đảm nhanh hơn check bằng browser vài lần, vài chục lần nhóe... Cũng giống như lúc refactor code, cũng được lợi ích như việc check trước khi bàn giao.

  • Mình đang làm engineer cho trang web http://www.blurb.com/ của US. Đây là trang web viết từ thời Rails 2 và tồn tại cả 10 năm rồi, phát triển dự án theo hướng TDD. Khi làm với 1 cái code base mà nó bự vkl(vãi khủng long) như vậy mà không có tests, viết code ko chuẩn, ko document đàng hoàng thì mình không tin là mình có khả năng nhảy vào project ngay lập tức được. Việc run hết cái test thôi đã lồi con mắt rồi, chứ đừng nói chạy ra browser verify từng cái coi nó có sót case nào ko, nó có break features nào ko, vkl lắm.

5) Bai bai

lucas2804 25-03-2017

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

lucas2804

1 bài viết.
0 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Bài viết liên quan
White
0 2
fCC: Technical Documentation Page note So I have finished the HTML part of this exercise and I want to come here to lament about the lengthy HTML ...
HungHayHo viết 27 ngày trước
0 2
White
19 1
Toán tử XOR có tính chất: + A XOR A = 0 + 0 XOR A = A Với tính chất này, có thể cài đặt bài toán sau với độ phức tạp O(N) về runtime, và với O(1)...
kiennt viết gần 2 năm trước
19 1
White
1 1
Chào mọi người, hôm nay mình viết một bài TIL nhỏ về cách lấy độ phân giải của màn hình hiện tại đang sử dụng. xdpyinfo | grep dimensions Kết quả...
namtx viết 12 tháng trước
1 1
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
1 bài viết.
0 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!