### 1. BDD là gì? Phát triển hướng theo hành vi (BDD) là một hoạt động phát triển phần mềm làm việc trong một vòng lặp phản hồi ngắn, nơi chúng ta luôn áp dụng phát triển thử nghiệm cho mọi tính năng mới mà chúng ta đang khám phá và làm việc. Để có phần giới thiệu chi tiết về thực tiễn của BDD, bạn nên đọc bài viết trước của tôi về phát triển dựa vào hành vi (Behavior-Driven Development). Bài viết này tập trung vào việc áp dụng các nguyên tắc này trong thực tiễn phát triển một ứng dụng web sử dụng Ruby on Rails. Như bài viết trước thì mình đã giới thiệu tổng quan cho các bạn thế nào là BDD, tác dụng của BDD còn nay thì sẽ tiếp tục nói về cách áp dụng BDD nhé! Tool sử dụng đó là Cucumber. ### 2. Công cụ viết BDD (Cucumber) cho ứng dụng Rails **2.1. Cucumber** Công cụ của chúng ta sẽ là Cucumber Cucumber là một công cụ kiểm thử tự động dựa trên việc thực thi các functions được mô tả dướng dạng plain-text, mục đích là để support cho việc viết Behavior Driven Development (BDD) của các developers. Điều này có nghĩa rằng kịch bản test unit (scenarios) sẽ được viết trước và thể hiện nghiệp vụ, sau đó source code mới được cài đặt để pass qua tất cả các stories đó. Ngôn ngữ được cucumber sử dụng là “Gherkin” (sẽ giới thiệu vào mục 3). Bản chất Cucumber được viết bằng Ruby nhưng chúng ta có thể sử dụng để test code được viết bằng Ruby, Java... Yêu cầu khi viết Cucumber là chúng ta có một chút ít hiểu biết về ngôn ngữ Ruby cơ bản Sau khi nghe giải thích có chắc nhiều người vẫn chưa hiểu được ấy nhỉ? Đại loại là thay vì cắm đầu vào viết code thì chúng ta sẽ viết các kịch bản test unit (scenarios) để thể hiện nghiệp vụ, sau đó mới viết code để pass qua tất cả các stories đó như giới thiệu ở bài trước. **2.2. Cài đặt** - Install cucumber gem trên ứng dụng bằng cách chạy câu lệnh: ``` gem install cucumber ``` - Thêm các gem sau vào Gemfile: ``` group :development, :test do gem "rspec-rails" gem 'cucumber-rails' gem 'database_cleaner' end ``` – Sau đó chạy ```bundle install ```và ```rails g cucumber:install``` – Khi cài đặt thành công sẽ sinh ra thư mục features chứa 2 thư mục con support (chứa file cấu hình env.rb và paths.rb) và step_definitions(sẽ chứa các Ruby code block thực thi các step). – Lưu ý: Trong file features/support/env.rb có dòng code: “DatabaseCleaner.strategy = :transaction” có nghĩa là trước khi chạy cucumber thì sẽ thực hiện truncate toàn bộ dữ liệu trong DB. Vì vậy bạn nên tạo 1 DB test riêng để chạy Cucumber, không chung DB với development hay production. Ok. như vậy là việc cài đặt và cấu hình Cucumber đã thành công. ### 3. Ngôn ngữ Gherkin. Ngôn ngữ sử dụng trong Cucumber đó là Gherkin . Các bạn có thể coi Cucumber là 1 chủng tộc thì Gherkin như kiểu ngôn ngữ chung để bọn nó giao tiếp với nhau ấy mà. Hay nói tổng quát hơn Gherkin là ngôn ngữ mà Cucumber có thể hiểu được. Gherkin là một ngôn ngữ thể hiện nghiệp vụ và có miền ngữ nghĩa xác định giúp cho người đọc có thể hiểu được kịch bản và hành động mà không cần biết chi tiết chúng được cài đặt như thế nào. Các quy tắc khi viết Gherkin: * Một file Gherkin chỉ mô tả cho một feature. * Source file Gherkin là .feature Trong file feature thì ta sẽ quy định các scenario và steps để thực thi các đoạn giao tiếp của cucumber. Khi chạy file source “.feature” mỗi step sẽ match với một đoạn code thực thi được định nghĩa sẵn trước đó gọi là “Step Definitions”. Các từ khóa chính trong Gherkin * Feature: Ý nghĩa: Là một đoạn text mô tả ngắn gọn về chức năng thực hiện * Background: Ý nghĩa: Cho phép thêm một số ngữ cảnh cho tất cả các Scenario trong feature Có chứa một số bước được chạy trước mỗi Scenario Có thể hiểu đơn giản giống như điều kiện tiên quyết để thực hiện tất cả các Scenario trong feature Được khai báo sau từ khóa “Feature” * Scenario: Ý nghĩa: Từ khóa bắt đầu trước mỗi kịch bản, tiếp theo là tiêu đề của kịch bản sẽ thực hiện Mỗi kịch bản bao gồm một hoặc nhiều bước * Given: Ý nghĩa: Mô tả điều kiện tiên quyết để thực hiện 1 Scenario * When: Ý nghĩa: Mô tả các hành động chính (Steps) mà người dùng thực hiện * Then: Mô tả: Mô tả kết quả đầu ra mong muốn của Scenario * And/ But: Ý nghĩa: Thay thế cho các từ khóa Given/ When/ Then để làm cho chương trình mạch lạc hơn * Scenario Outlines: Ý nghĩa: Scenario Outlines để gom nhóm các kịch bản có chung các Steps nhưng có nhiều input và output Examples để thực hiện khai báo các giá trị cho các biến trong Scenario Outlines * @tag: Ý nghĩa: Sử dụng @tag để tổ chức, sắp xếp các tính năng và kịch bản ### 4. Cách viết BDD **Đối với file feature** Feature tốt nhất là được mô tả ngắn gọn, không rườm rà, sướt mướt, than nghèo kể khổ, vì vậy điều kiện đầu tiên cho feature đó là mô tả phạm vi và nội dung chức năng trong 1 câu duy nhất, chỉ 1 câu duy nhất :3 * Thống nhất 1 format chuẩn Việc áp dụng một format chuẩn sẽ giúp những người tham gia vào dự án sau có thể dễ dàng hiểu được feature. ``` Feature: [One line describing the story] [Optional — Feature description] Scenario: [One line describing the scenario] Given [context] And [some more context]… When [event] Then [outcome] And [another outcome]… ``` **Đối với background** **- Giữ cho background ngắn gọn** Background không nên dài quá 4 dòng và nên được hiểu như một điều kiện tiên quyết cho mỗi scenario. Điều này làm cho người đọc cái feature của mình, luôn giữ được đầu óc thông thoáng không phải quan tâm cái background làm gì ví như cái việc mình Given một người dùng đã đăng nhập thành công **- Không để các công việc liên quan đến kĩ thuật trong background** **- Không bao giờ sử dụng tag trong background** **- Không bao giờ sử dụng background cho file feature mà chỉ có một scenario** **- Tiếp theo là scenarios và steps** **- Sử dụng GIVEN-WHEN-THEN đúng thứ tự** Luôn luôn bắt đầu scenario với GIVEN thậm chí là sử dụng GIVEN trong background. Scenario sẽ khó đọc hơn nhiều nhiều đấy nếu bạn đưa thứ tự các GIVEN-WHEN-THEN lung tung **- Giữ cho scenario độc lập** Scenario nên chạy độc lập mà không phụ thuộc vào bất cứ scneario nào khác. Điều này giúp cho quá trình debug nhanh hơn khi có cái gì đó nó sai sai mà anh em ta cảm nhận được **- Tránh conjunctive steps** Gherkin có các AND và BUT để cho chúng ta sử dụng do đó cần tránh việc viết steps mà include các từ ANd hay BUT vào mà không nằm ở đầu Step **- Scenario cho cả các case fail** Đặc biệt lưu ý việc cài đặt feature phải đúng với các giá trị business để có thể đảm bảo cho việc cover được đầy đủ các trường hợp từ valid input đến invalid input **- DRY: Dont repeat you** Luôn luôn tìm kiếm cách để tái sử dụng (refactor) lại đống steps mà không phụ thuộc vào feature. **- Cuối cùng thì là tags** Tagging giúp chúng ta quản lý, sắp xếp được feautre và scenario trong các project BDD. Các feature files sẽ nằm rải rác ở rất nhiều chỗ trong thư mục A, thư mục B hoặc thư mục C; dùng tag sẽ giúp ta filter được ### 5. Demo Viết BDD cho chức năng login 1. Tạo file features/login.feature với nội dung: ``` Feature: Login form Input data to form click submit button Scenario: Sends a contact message Given I am a user_super Given I am on the signin page When I fill in "session[email]" with "super@septeni-technology.jp" When I fill in "session[password]" with "xxxxxxxxxx" When I press "Login" Then I should be on the clients page ``` – Trong đó: + Given I am a user_super: đăng nhập với tài khoản super + Given I am on the signin page: access tới trang login + session[email]: tên của email tag + session[password]: tên của password tag + When I press “Login”: mô tả sự kiện click vào nút có value=”Login” + Then I should be on the clients page: đăng nhập thành công và chuyên đến trang clients 2. Chạy Cucumber để thấy code feature - Tạo 1 file login_steps.rb và coppy nội dung vào, ta có: ``` Given(/^I am a user_super$/) do pending # Write code here that turns the phrase above into concrete actions end Given(/^I am on the signin page$/) do pending # Write code here that turns the phrase above into concrete actions end When(/^I fill in "([^"]*)" with "([^"]*)"$/) do |arg1, arg2| pending # Write code here that turns the phrase above into concrete actions end When(/^I press "([^"]*)"$/) do |arg1| pending # Write code here that turns the phrase above into concrete actions end Then(/^I should be on the clients page$/) do pending # Write code here that turns the phrase above into concrete actions end ``` 3. Chạy lênh Cucumber dẫn đến feature cần chạy, ta sẽ có kết quả ``` $cucumber feature/login.feature ``` Kết quả nhận đươc ``` 1 scenario (1 passed) 6 steps (6 passed) 0m0.903s ``` Tham khảo: http://labs.septeni-technology.jp/bdd/kiem-thu-tu-dong-su-dung-bdd-2/ https://semaphoreci.com/community/tutorials/applying-bdd-to-ruby-on-rails-web-applications https://code4shares.wordpress.com/2016/08/01/ap-dung-bdd-cho-du-an-agile-phan-3/