Rails – Functional Testing


Được đăng vào ngày 18/11/2016 | 0 bình luận
Rails – Functional Testing
5 (100%) 4 votes

Trong bài Unit Testing chúng ta đã tìm hiểu cách kiểm tra code đối với model, trong phần này chúng ta sẽ tìm hiểu cách kiểm tra code trên controller – hay còn gọi là Functional Testing.

Khi chúng ta tạo một controller thì Rails sẽ tự động tạo cho chúng ta một file dùng để test controller này, trong bài Routing chúng ta đã tạo một controller là store, và Rails sẽ tạo một file test tương ứng có tên là store_controller_test.rb trong thư mục test/controllers. Ngoài ra nếu bạn còn nhớ thì khi tạo một model, Rails cũng sẽ định nghĩa controller cho model này, và dĩ nhiên Rails cũng sẽ tạo một file test cho controller này, chúng ta đã tạo model Product và file test controller là file products_controller_test.rb cũng nằm trong thư mục test/controllers.

capture

Nội dung của file products_controller_test.rb như sau:

require 'test_helper'

class ProductsControllerTest < ActionController::TestCase
    setup do
        @product = products(:one) 
    end

    test "should get index" do
        get :index
        assert_response :success
        assert_not_nil assigns(:products)
    end

    test "should get new" do
        get :new
        assert_response :success
    end

    test "should create product" do
        assert_difference('Product.count') do 
             post :create, product: { 
                     description: @product.description, 
                     image_url: @product.image_url, 
                     price: @product.price, 
                     title: @product.title } 
        end

        assert_redirected_to product_path(assigns(:product))
    end

    test "should show product" do
         get :show, id: @product
         assert_response :success
    end

    test "should get edit" do
         get :edit, id: @product
         assert_response :success
    end

    test "should update product" do
         patch :update, id: @product, product: { 
                 description: @product.description, 
                 image_url: @product.image_url, 
                 price: @product.price, 
                 title: @product.title }   
         assert_redirected_to product_path(assigns(:product)) 
    end

    test "should destroy product" do
         assert_difference('Product.count', -1) do
             delete :destroy, id: @product
         end
 
         assert_redirected_to products_path
     end
end

Trong đó đoạn setup do...end sẽ làm công việc khởi tạo, ở đây Rails khởi tạo biến @product là đối tượng one trong fixture.

Sau đó là các đoạn code test, ở đây các phương thức get, post, patch sẽ gửi các gói tin HTTP lên server, theo sau là tên các hàm trong model mà Rails đã định nghĩa trước, như index là phương thức index trong controller, các phương thức gửi sẽ tương ứng với một phương thức của giao thức HTTP như GET, POST, PATCH, PUT… sau đó chúng ta dùng các phương thức assert để kiểm tra dữ liệu trả về từ server, trong đó:

  • assert_response: nhận vào tham số mã và kiểm tra xem gói tin trả về có mã giống với tham số hay không, ở đoạn code trên các đoạn assert_response đều kiểm tra với mã là :success, tương ứng với mã từ 200-299, ngoài ra còn có :redirect tương ứng với mã từ 300-399, :missing là mã 404, :error là từ 500-599.
  • assert_not_nil: nhận vào một đối tượng và kiểm tra xem đối tượng đó có giá trị là nil hay không
  • assert_difference: nhận vào tham số là một biểu thức (phép tính, hàm, toán tử…v.v) và tính với hiệu của biểu thức đó sau khi thực hiện phần code phía sau, rồi kiểm tra xem hiệu đó có bằng 1 hay không.
  • assert_redirected_to: nhận vào tham số là một URL, kiểm tra xem URL đó có giống với hành động chuyển hướng trang web cuối cùng được thực hiện hay không.

ProductsControllerTest

Bạn có thể chạy lệnh rake test:functionals để Rails chạy những test trên. Và Bạn sẽ nhận được 2 lỗi tại test "should create product""should update product". 

Đối với "should create product" thì lý do là vì bị trùng thuộc tính title, chúng ta đã quy định thuộc tính này phải là duy nhất, nhưng Rails lại lấy dữ liệu từ fixture để chèn vào CSDL nên mới bị trùng, và Rails lại không kiểm tra trường hợp bị trùng, chúng ta có thể sửa lại như sau:

require 'test_helper'

class ProductsControllerTest < ActionController::TestCase
    .
    .
    .
    test "should create product" do 
        assert_difference('Product.count', 0) do 
            post :create, product: { 
                    description: @product.description, 
                    image_url: @product.image_url, 
                    price: @product.price,
                    title: @product.title } 
        end
 
        assert_difference('Product.count') do
            post :create, product: {
                    :description => 'Some new book',
                    :image_url => 'some_img.jpg',
                    :price => 59.99,
                    :title => 'Some new title'
            } 
        end
        assert_redirected_to product_path(assigns(:product))
    end
    .
    .
    .
end

Đối với trường hợp bị trùng, chúng ta truyền thêm tham số thứ 2 vào phương thức assert_difference, đây là tham số hiệu, như đã nói ở trên mặc định phương thức này kiểm tra hiệu bằng 1, ở đây chúng ta kiểm tra hiệu bằng 0, tức là nếu không chèn thêm bản ghi mới vào thì số lượng các bản ghi không đổi, còn với trường hợp chèn thành công thì chúng ta kiểm tra một yêu cầu redirect có tồn tại không.

Tương tự, "should update product" cũng bị 2 lỗi vì Rails chưa kiểm tra trường hợp cập nhật thất bại, nếu bạn còn nhớ thì trong Fixtures có 2 đối tượng dữ liệu mẫu có title giống nhau, và cả 2 đều có thuộc tính image_url không có đuôi hợp lệ, chúng ta sửa lại như sau:

# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

one:
 title: MyString
 description: MyText
 price: 9.99
 image_url: MyString

two:
 title: MyString2
 description: MyText
 price: 9.99
 image_url: MyString.jpg

ruby:
 title: Ruby on Rails 4
 description: Zzzzzzz....
 price: 9.99
 image_url: img.jpg

Chúng ta sửa dữ liệu của two sao cho hợp lệ, còn one thì giữ nguyên. Tiếp theo chúng ta sửa đoạn code test như sau:

class ProductsControllerTest < ActionController::TestCase
    .
    .
    .
    test "should update product" do
        patch :update, id: @product, product: { 
            description: @product.description, 
            image_url: @product.image_url, 
            price: @product.price, 
            title: @product.title 
        }
        assert_response 200
 
        @product = products(:two)
        patch :update, id: @product, product: {
            :description => @product.description,
            :image_url => @product.image_url,
            :price => @product.price,
            :title => @product.title
        } 
        assert_redirected_to product_path(assigns(:product))
     end
    .
    .
    .
end

Đầu tiên chúng ta kiểm tra đối với fixture mặc định được tạo trong phần setup là  one, đây là dữ liệu không hợp lệ, Rails sẽ trả về response có thông báo lỗi, chú ý là response ở đây mang theo một thuộc tính thông báo lỗi, còn bản thân response lại là một gói tin HTTP được gửi thành công, nên chúng ta mới so sánh với mã trạng thái là 200. Sau đó chúng ta đổi lại biến @product lấy giá trị của fixture two, đây là fixture hợp lệ, khi cập nhật xong thì trang web của chúng ta sẽ được chuyển đến đường dẫn /products/<id>, do đó chúng ta kiểm tra với phương thức assert_redirected_to.

StoreControllerTest

Đối với controller store chỉ có một phương thức index thì có file test như sau:

require 'test_helper'

class StoreControllerTest < ActionController::TestCase
    test "should get index" do
        get :index
        assert_response :success
    end

end

Ở đây Rails chỉ kiểm tra xem khi trỏ đến hàm index này thì có nhận được một gói tin trả về với mã từ 200-299 (thành công – success) hay không.

Chúng ta kiểm tra thêm một số thứ bằng cách sửa lại như sau:

require 'test_helper'

class StoreControllerTest < ActionController::TestCase
    test "should get index" do
        get :index
        assert_response :success
        assert_select '#columns #side a', :minimum => 4
        assert_select '#main .entry', 3
        assert_select 'h3', 'Ruby on Rails 4'
        assert_select '.price', /\$[,\d]+\.\d\d/
    end
end

Phương thức assert_select sẽ kiểm tra xem trong nội dung HTML trả về có chứa những thành phần mà chúng ta quy định hay không.

Chẳng hạn như assert_select '#columns #side a', tức là kiểm tra xem trong nội dung có thẻ nào có id là columns hay không, và trong đó phải có một thẻ có id là side, rồi phải có thẻ <a>, ngoài ra thuộc tính minimum còn yêu cầu là phải có ít nhất 4 thẻ <a>.

Dòng assert_select '#main .entry', 3 yêu cầu nội dung trả về phải có một thẻ có id là main, trong đó có 3 thẻ có class là entry.

Dòng assert_select 'h3', 'Ruby on Rails 4' kiểm tra xem có thẻ <h3> nào có nội dung là Ruby on Rails 4 hay không.

Cuối cùng dòng assert_select '.price', /\$[,\d]+\.\d\d/ kiểm tra xem thẻ nào mà có class là price có nội dung khớp với đoạn biểu thức chính quy hay không, ở đây đoạn biểu thức chính quy này mô tả số tiền, có ký tự $, có 2 chữ số sau phần thập phân.

Bạn có thể chạy lệnh rake test:functionals để Rails kiểm tra tất cả các đoạn test trên. Có tổng cộng 8 đoạn test trong 2 lớp StoreControllerTestProductsControllerTest.

C:\Project\Rails\depot>rake test:functionals
Run options: --seed 46620

# Running:

........

Finished in 0.874736s, 9.1456 runs/s, 21.7208 assertions/s

8 runs, 19 assertions, 0 failures, 0 errors, 0 skips






Trả lời


Lưu ý: bọc code trong cặp thẻ [code language="x"][/code] để highlight code.


Ví dụ:


[code language="cpp"]


    std::cout << "Hello world";


[/code]



Các ngôn ngữ được hỗ trợ gồm: actionscript3, bash, clojure, coldfusion, cpp, csharp, css, delphi, diff, erlang, fsharp, go, groovy, html, java, javafx, javascript, latex, matlab, objc, perl, php, powershell, python, r, ruby, scala, sql, text, vb, xml.

Thư điện tử của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *