- vừa được xem lúc

[RubyonRails][Rspec] Unit Test trong rails (Phần 2)

0 0 9

Người đăng: Ngô Quý 𝕋𝕣ườ𝕟𝕘

Theo Viblo Asia

Trong phần trước mình đã giới thiệu sương sương về Rspec trong Rails và một số gem hỗ trợ, và trong bài viết này mình sẽ nói nhiều hơn về những thành phần cơ bản khi viết Rspec

Sử dụng describe , context, it

Trong RSpec, describe, context, và it là các phương thức được sử dụng để viết và tổ chức các test case một cách rõ ràng và dễ đọc.

  • describe: describe được sử dụng để nhóm các test case lại với nhau dưới một tiêu đề chung, thường là để mô tả một đối tượng, một class, hoặc một phương thức đang được kiểm tra. Nó giúp tổ chức test suite của bạn thành các phần nhỏ, dễ quản lý và hiểu.

    describe "Tên đối tượng hoặc phần của mã nguồn cần kiểm tra" do # Các test case liên quan đến đối tượng hoặc phần mã nguồn này sẽ được định nghĩa ở đây
    end
    
  • context: context được sử dụng để tạo ra một bối cảnh hoặc điều kiện cụ thể cho các test case. Thường được sử dụng để mô tả các trạng thái hoặc điều kiện khác nhau mà đối tượng cần được kiểm tra.

    context "Khi điều kiện hoặc trạng thái nhất định xảy ra" do # Các test case liên quan đến điều kiện hoặc trạng thái này sẽ được định nghĩa ở đây
    end
    
  • it: it được sử dụng để mô tả một test case cụ thể, trong đó bạn định nghĩa điều kiện cần kiểm tra và kết quả mong đợi của test case đó.

    it "Mô tả điều kiện cần kiểm tra" do # Mã kiểm tra và kết quả mong đợi sẽ được định nghĩa ở đây
    end
    

Và thứ tự của 3 thằng này theo chuẩn của nó là describe -> context -> it

describe 'User entity testing ' do context 'when creating a new user' do it 'is valid with valid attributes' do # Kiểm tra tính hợp lệ của user khi tạo mới end it 'is not valid without a name' do # Kiểm tra user không hợp lệ nếu thiếu thuộc tính name end end context 'when updating an existing user' do it 'updates the user attributes' do # Kiểm tra cập nhật thuộc tính của user thành công end it 'does not allow invalid attributes' do # Kiểm tra user không được cập nhật nếu dữ liệu mới không hợp lệ end end
end 

let , let!, beforesubject

Trong quá trình viết RSpec có một số phương thức thường sử dụng để thiết lập trạng thái ban đầu và các biến trong các test case và làm cho code Rspec của bạn trở nên dễ hiểu hơn!

  • let: let được sử dụng để định nghĩa một biến instance trong toàn bộ phạm vi của một describe hoặc một context. Biến được tạo bởi let chỉ được khởi tạo khi nó được gọi đến lần đầu tiên trong mỗi test case.

    describe User do let(:user) { User.new(name: 'John', email: 'john@example.com') } it 'has a name' do expect(user.name).to eq('John') end it 'has an email' do expect(user.email).to eq('john@example.com') end
    end
    
  • let!: let! tương tự như let, nhưng nó sẽ tạo và khởi tạo biến ngay khi một describe hoặc một context được gọi. Điều này có nghĩa là biến được tạo bởi let! sẽ được khởi tạo trước mỗi test case.

    describe User do let!(:user) { User.create(name: 'John', email: 'john@example.com') } it 'has a name' do expect(user.name).to eq('John') end it 'has an email' do expect(user.email).to eq('john@example.com') end
    end 
  • before: before được sử dụng để thiết lập trạng thái ban đầu hoặc thực hiện các hành động trước khi mỗi test case chạy. Nó thường được sử dụng để thực hiện các thao tác chuẩn bị dữ liệu trước khi test case được thực hiện.

    describe User do before do @user = User.new(name: 'John', email: 'john@example.com') end it 'has a name' do expect(@user.name).to eq('John') end it 'has an email' do expect(@user.email).to eq('john@example.com') end
    end
    
  • subject: subject được sử dụng để định nghĩa một biến instance mặc định mà được sử dụng trong các test case. Khi không có subject được gọi, RSpec sẽ sử dụng biến mặc định của subject là biến subject.

    subject trong RSpec là một cơ chế mạnh mẽ cho phép bạn định nghĩa một biến hoặc một đối tượng mặc định để sử dụng trong các test case của một describe hoặc một context. subject giúp làm cho code test của bạn trở nên ngắn gọn và dễ đọc hơn bằng cách loại bỏ sự lặp lại và làm tăng tính rõ ràng của mã.

    1. Định nghĩa subject cơ bản
      
      describe User do subject { User.new(name: 'John', email: 'john@example.com') } it 'has a name' do expect(subject.name).to eq('John') end it 'has an email' do expect(subject.email).to eq('john@example.com') end
      end
      

      Trong ví dụ này, subject được định nghĩa là một đối tượng User mới với các thuộc tính được chỉ định. Trong mỗi test case, chúng ta sử dụng subject để tham chiếu đến đối tượng này.

    2. Sử dụng subject với biến mặc định
      

      Nếu không có subject được định nghĩa trong một describe, RSpec sẽ tự động tạo ra một subject mặc định từ tên của describe.

      describe User do it 'has a name' do expect(subject.name).to eq('John') end it 'has an email' do expect(subject.email).to eq('john@example.com') end
      end
      

      Trong trường hợp này, subject sẽ trở thành một đối tượng User mới tự động được tạo ra bởi RSpec.

    3. Sử dụng subject với biến đặc biệt
      

      Bạn có thể đặt tên cho subject và sử dụng nó trong toàn bộ describe hoặc context.

      describe User do subject(:custom_user) { User.new(name: 'Jane', email: 'jane@example.com') } it 'has a name' do expect(custom_user.name).to eq('Jane') end it 'has an email' do expect(custom_user.email).to eq('jane@example.com') end
      end
      

      Trong trường hợp này, custom_user được định nghĩa là một biến subject với tên là custom_user, và bạn có thể sử dụng nó trong các test case trong describe.

      Một ví dụ khác khi dùng subject. Giả sử bạn đang viết Rspec cho controller và đang viết testcase cho phương thức Create với method (Post) thì bạn hoàn toàn có thể viết như sau:

      describe 'POST #create' do subject(:action) { post user_path, params: params } context 'mô tả context 1' do # thay đổi params linh hoạt để phù hợp với testcase  let(:params) { { user_atrributes: valid_params } } it 'Mô tả điều kiện cần kiểm tra' do action expect(response).to ... end end context 'mô tả context 2' do # thay đổi params linh hoạt để phù hợp với testcase  let(:params) { { user_atrributes: valid_params } } it 'Mô tả điều kiện cần kiểm tra' do expect ... end it 'Mô tả điều kiện cần kiểm tra' do action expect(response).to ... end end context 'mô tả context 3' do # thay đổi params linh hoạt để phù hợp với testcase  let(:params) { { user_atrributes: invalid_params } } it 'Mô tả điều kiện cần kiểm tra' do expect ... end it 'Mô tả điều kiện cần kiểm tra' do action expect(response).to ... end end end
      

Mocking và Stubbing

MOCKSTUBS là hai kỹ thuật quan trọng trong unit testing để giả lập hoặc thay thế các thành phần phụ thuộc (dependencies) của một phần của mã nguồn mà bạn đang kiểm tra. Điều này giúp bạn kiểm tra chức năng của một phần mã nguồn mà không phụ thuộc vào hành vi hoặc trạng thái của các thành phần khác

Giả sử bạn có 1 class Calculator và có phương thức add

class Calculator def add(a, b) return a + b end
end
  • Stubbing

    allow(some_object).to receive(some_method).and_return(some_value)

    Trong stubbing, chúng ta giả lập hoặc thay thế một phần của mã nguồn để kiểm tra hành vi của mã nguồn khác. Trong ví dụ này, chúng ta sẽ sử dụng stubbing để thay thế phương thức add của class Calculator.

    require 'calculator' RSpec.describe Calculator do describe '#add' do it 'adds two numbers' do calculator = Calculator.new allow(calculator).to receive(:add).and_return(5) result = calculator.add(2, 3) expect(result).to eq(5) end end end
    

    Ở đây, chúng ta sử dụng phương thức allow của RSpec để mock phương thức add của đối tượng calculator và trả về giá trị 5. Điều này giả lập việc thực thi của phương thức add mà không cần dùng tới implement thực tế của nó.

  • Mocking

     `expect(some_object).to receive(some_method).and_return(some_value)`
    

    Về cơ bản thì Mocking và Stubbing khá giống nhau chỉ khác nhau ở chỗ:

    1. Mock thể hiện những sự mong đợi khi gọi đến một method nào đó, nó có thể là kết quả trả về, là đối số truyền vào method đó hay là số lần gọi đến method đó.
    2. Trong khi đó Stub chỉ như là định nghĩa/cho phép một object gọi đến một method và trả về một kết quả nào đó

    Ngoài allowexpect chúng ta còn có các phương thức hay dùng, các bạn tham khảo tại đây

Sử dụng Factory Bot hiệu quả

  • Cơ bản về Factory Bot

    Factory Bot là một thư viện giúp tạo ra dữ liệu mẫu cho các test case một cách linh hoạt và dễ dàng. Bằng cách sử dụng Factory Bot, bạn có thể tạo ra các đối tượng và dữ liệu liên quan một cách tự động, giúp rút ngắn thời gian viết test case và tạo ra các trường hợp test phong phú.

  • Nâng cao với Factory Bot

    1. Sử dụng Traits

      Traits là một tính năng mạnh mẽ của Factory Bot cho phép bạn định nghĩa các thuộc tính và hành vi đặc biệt cho các factory. Điều này giúp tạo ra các phiên bản của cùng một đối tượng với các thuộc tính khác nhau dễ dàng hơn.

    2. Kết hợp với Faker

      Faker là một thư viện giúp tạo ra dữ liệu giả mạo một cách tự động. Kết hợp Factory Bot với Faker giúp bạn tạo ra các dữ liệu thực tế và phong phú hơn cho các test case của mình.

  • Ví dụ về Sử dụng Factory Bot

    Giả sử bạn có một model User trong ứng dụng của mình và bạn muốn viết test case cho nó bằng cách sử dụng Factory Bot. Dưới đây là một ví dụ về cách sử dụng Factory Bot để tạo ra các đối tượng User trong test case:

    # spec/factories/users.rb FactoryBot.define do factory :user do name { Faker::Name.name } email { Faker::Internet.email } password { Faker::Internet.password } trait :admin do admin { true } end end end
    

    Trong ví dụ trên, chúng ta định nghĩa một factory cho đối tượng User với các thuộc tính name, email và password được tạo ra bằng cách sử dụng Faker. Ngoài ra, chúng ta cũng định nghĩa một trait là :admin để tạo ra các user có quyền admin.

    Sau đó, bạn có thể sử dụng factory này trong test case của mình như sau:

    # spec/models/user_spec.rb require 'rails_helper' RSpec.describe User, type: :model do it 'is valid with valid attributes' do user = FactoryBot.create(:user) expect(user).to be_valid end it 'is an admin' do admin_user = FactoryBot.create(:user, :admin) expect(admin_user.admin).to eq(true) end end
    

Kết luận

Trên đây là những hiểu biết của mình về cách thực hiện unit testing trong Rails sử dụng RSpec. Hi vọng những chia sẻ ở trên sẽ giúp ích được cho bạn đọc khi bắt đầu dự án Ruby on Rails của mình.

Tài liệu tham khảo

https://rubydoc.info/gems/rspec-mocks/frames

https://www.martinfowler.com/articles/mocksArentStubs.html

Bình luận

Bài viết tương tự

- vừa được xem lúc

テストカバレッジの概念の紹介(C0/C1/C2)

C0/C1/C2カバレッジとは. テストカバレッジがどんなものかは、他の記事を読んでください。. その上で、テストケースの分類―C0,C1,C2について説明します。. 以下のようなコードのテストケースを考えて見ます。.

0 0 258

- vừa được xem lúc

Kiểm thử đơn vị - Tầm quan trọng của nó trong kiểm thử phần mềm là gì?

Kiểm thử đơn vị là gì. .

0 0 59

- vừa được xem lúc

Laravel: Một số phương thức viết Unit Test cho các ca siêu khó

Trong quá trình viết Unit Test cho các dự án chắc hẳn bạn sẽ gặp phải một số ca Unit Test cực khó, hoặc là bạn phải refactor lại khá nhiều, hoặc là phải chuyển qua viết Feature Test. Nhưng không phải lúc nào Feature Test cũng hoạt động, ví dụ như bạn cần viết test cho Jobs hoặc Models chẳng hạn.

0 0 152

- vừa được xem lúc

Chìa khóa kiểm thử đơn vị thành công - Làm thế nào các nhà phát triển kiểm thử mã code của họ?

Người kiểm thử Hộp đen không quan tâm đến kiểm thử đơn vị. Mục tiêu chính của họ là xác thực ứng dụng so với các yêu cầu mà không cần đi sâu vào chi tiết triển khai.

0 0 91

- vừa được xem lúc

Unit test căn bản - Vài khái niệm bước đầu về testing

Hẳn mọi developer đều từng nghe về unit test và những lợi ích nó mang lại. Mình cũng vậy, dạo gần đây mình đã bắt đầu tìm hiểu về unit test và áp dụng cho project của mình.

0 0 165

- vừa được xem lúc

Testing with JUnit in Java

1.Giới thiệu về JUnit.

0 0 94