Để vâ ̣n du ̣ng kỹ thuâ ̣t phát triển phần mềm hƣớng hành vi và công cụ Behat vào việc xây dựng ứng dụng web , sau khi cài đă ̣t Behat và Mink , ta cho ̣n mô ̣t thƣ mu ̣c để thƣ̣c hiê ̣n viê ̣c kiểm thƣ̉ ƣ́ng du ̣ng và go ̣i lê ̣nh bin/behat -–init. Lê ̣nh này ta ̣o thƣ mu ̣c features để lƣu các đi ̣nh nghĩa tính năng là các tập tin .feature, features/bootstrap/FeatureContext.php là tâ ̣p tin đi ̣nh nghĩa lớp ngƣ̃ cảnh FeatureContext để viết các định nghĩa bƣớc . Lớp ngƣ̃ cảnh ban đầu đƣợc tạo ra có nội dung nhƣ sau:
class FeatureContext extends BehatContext { /**
* Initializes context.
* Every scenario gets it's own context object.
* @param array $parameters context parameters (set them up through behat.yml)
public function __construct(array $parameters) {
// Initialize your context here }
}
Tiếp theo ta đi ̣nh nghĩa các cấu hình cho Behat làm viê ̣c ta ̣i file behat .yml, có nội dung nhƣ sau:
Hình 4-1 Cấu hình ứng dụng
Các khối cấu hình cơ bản đã đƣợc nêu trong mục3.5. Trong tâ ̣p tin cấu hình của ứng dụng đang thực hiện chỉ chƣ́a các cấu hình đơn gi ản nhƣ vi ̣ trí lƣu các đi ̣nh nghĩa tính năng , vị trí của lớp ngữ cảnh , sƣ̉ du ̣ng thành phần mở rô ̣ng MinkExtension, và cấu hình địa chỉ ứng dụng đƣợc kiểm thƣ̉. Các tham số của cấu hình sẽ đƣợc Behat tƣ̣ đô ̣ng chuyển thành các tham số của lớp ngƣ̃ cảnh.
4.2. Tiến trình thực hiện ứng dụng
Đối với mỗi dự án áp dụng các phƣơng pháp phát triển phần mềm hiện đại , hê ̣ thống thƣờng đƣợc phát triển thông qua nhiều vòng lă ̣p khác nhau. Trong mỗi vòng lặp,kỹ thuật phát triển phần mềm hƣớ ng hành vi thƣờng thƣ̣c hiê ̣n qua bốn giai đoa ̣n chính gồm: đi ̣nh nghĩa tính năng, viết các đi ̣nh nghĩa bƣớc, cài đặt ứng dụng, điều chỉnh mã nguồn nhƣ Hình 4-2.
1) Đi ̣nh nghĩa các tính năng
Tính năng là yếu tố quan trọng của kỹ thuật phát triển phần mềm hƣớng hành vi. Tính năng mô tả một chức năng tƣơng đối hoàn chỉnh của hệ thống , và đƣợc viết bằng ngôn ngƣ̃ Gherkin. Ở vòng lặp đầu tiên của hệ thống, không nhất thiết phải đƣa ra tất cả các tính năng , chúng có thể đƣợc bổ sung và điều chỉnh dần trong quá trình phát triển hê ̣ thống . Trƣớc mỗi vòng lă ̣p , khách hàng cùng với đô ̣i phát triển sẽ viết các tính năng theo đúng quy c ách của BDD, nó mô tả các hành vi của hệ thống dƣới dạng các tiêu chí có thể đƣa vào kiểm thử chấp nhâ ̣n tƣ̣ đô ̣ng.
2) Viết cácđi ̣nh nghĩa bước
Quá trình thực hiện kiểm thử , Behat sẽ duyê ̣t lớp ngƣ̃ cảnh FeatureContext để tìm biểu thƣ́c chính quy tƣơng ƣ́ng với tƣ̀ng bƣớc và thƣ̣c hiê ̣n phƣơng thƣ́c kiểm thƣ̉ của chúng nhằm đƣa ra kết quả. Trong trƣờng hợp không tìm thấy đi ̣nh nghĩa bƣớc tƣơng ứng, Behat sẽ tƣ̣ đô ̣ng sinh ra các khung đi ̣nh nghĩa bƣớc. Mỗi định nghĩa bƣớcbao gồm mô ̣t biểu thƣ́c chính quy và mô ̣t phƣơng thƣ́c kiểm thƣ̉ với các tham số tƣơng ƣ́ng mô tả bƣớc . Ở trạng thái ban đầu , các phƣơng thức
kiểm thƣ̉ thƣờng trả ra ngoa ̣i lê ̣ PenddingException nhằm đánh dấu bƣớc n ày ở trạng thái chờ định nghĩa và lập trình viên cần bổ sung các mã kiểm thử cho bƣớc đó.
Dƣ̣a vào các khung đi ̣nh nghĩa bƣớc do Behat sinh ra , chúng ta sẽ bổ sung mã nguồn thích hợp cho phƣơng thức kiểm thử . Phƣơng thức kiểm thử thƣờng đọc dữ liệu trên trang thông qua các phƣơng thức của bộ điều khiển trình duyệt, so sánh với dữ liệu mong muốn đƣợc viết trong phần mô tả bƣớc để đƣa ra kết quả kiểm thử.
Hình 4-2 Tiến hình áp du ̣ng BDD và Behat để phát triển ƣ́ng du ̣ng
Behat undefined>0 (2) (3) (4)
3) Cài đặt tính năng
Kỹ thuật phát triển phần mềm hƣớng hành vi yêu cầu các kiểm thử phải đƣợc viết trƣớc khi cài đă ̣t mã nguồn cho mỗi tính năng , do đó sau khi đi ̣nh nghĩa tính năng, viết các đi ̣nh nghĩa bƣớc , đô ̣i phát triển nên go ̣i Behat để đảm bảo các tính năng chƣa đƣợc cài đặt mã nguồn phải ở trạng thái thất bại.Sau đó sử dụng tài liệu đặc tả tính năng và mô tả ở phƣơng thức kiểm thử để cài đặt mã nguồn cho ứng dụng. Quá trình cài đặt kết thúc khi kiểm thử tính năng đó ở trạng thái thành công.
4) Điều chỉnh mã nguồn
Các hệ thống sử dụng kỹ thuật phát triển phần mềm hƣớng hành vi luôn luôn có sƣ̣ bổ sung và điều chỉnh mã nguồn nhằm mu ̣c đích làm cho tra ̣ng thái kiểm thƣ̉ tính năng đó thành công ở tất cả ki ̣ch bản . Sau mỗi lần bổ sung mã nguồn, ta cần phải điều chỉnh , tinh chỉnh mã nguồn cho ƣ́ng du ̣ng để nó trở nên rõ ràng, dễ đo ̣c. Viê ̣c điều chỉnh mã ngu ồn cần đảm bảo không làm ảnh hƣởng tới cài đă ̣t của các tính năng đã phát triển trƣớc đó.
4.3. Áp dụng phát triển hƣớng hành vi và công cụ Behat
Trình tự để phát triển ứng dụng bằng kỹ thuật phát triển phần mềm hƣớng hành vi đƣợc chia làm bốn giai đoa ̣n chính (Hình 4-2). Tuy nhiên, để ngƣời đọc nắm rõ quá trình phát triển ƣ́ng du ̣ng , trong phần này tác giả mô tả cách thƣ̣c hiê ̣n theo tƣ̀ng bƣớc nhỏ mô ̣t cho mô ̣t tính năng cu ̣ thể.
4.3.1. Tính năng “Xem trang chủ”
Kịch bản trang chủ: Trang chủ là đầu vào của hê ̣ thống , nó chứa các khối nô ̣i dung khác nhau đƣợc quy đi ̣nh bởi các css – id, trang chủ có cơ cấu các khối nhƣ sau:
TOP NAV
LEFT CONTENT RIGHT
FOOTER
Tính năng trang chủ (homepage) chƣ́a các ki ̣ch bản kiểm tra các khối nô ̣i dung của trang chủ nhƣ sau:
Feature: View homepage In order to view homepage As a visitor
I want to see homepage content block
Từ khóa Feature: dùng để xác định một tính năng , tƣ̀ khóa này nằm ở dòng đầu tiên của tập tin mô tả tính năng. Các dòng tiếp theo đƣợc viết theo định dạng mô tả một câu chuyện ngƣời dùng , chúng chỉ có tác dụng giải thích tính năng theo các khía ca ̣nh ngƣời dùng , ý nghĩa, hoạt động chứ không có vai trò trong quá trình kiểm thƣ̉ tƣ̣ đô ̣ng . Sau khi mô tả xong phần này , ta lần lƣợt đi ̣nh nghĩa các kịch bản sử dụng tính năng.
Kịch bản nền (Background):Tính năng trang chủ đều có ngữ cảnh chung là ngƣời dùng đang truy cập địa chỉ trang chủ (index.php) Ngữ cảnh này cần đƣợc định nghĩa trong mỗi kịch bản của tính năng “Xem trang chủ”. Để tránh lă ̣p la ̣i bƣớc này trên các kịch bản , ta khai báo nó ở ki ̣ch bản nền . Khi kiểm thƣ̉ mô ̣t tính năng, kịch bản nền sẽ đƣợc thực hiện trƣớc mỗi kịch bản thông thƣờng.
Background:
Given I am on homepage
Tiếp theo cần định nghĩa các kịch bản sử dụng cho tính năng này, việc phân chia các kịch bản tùy theo từng trƣờng hợp sử dụng hoặc chia vùng để kiểm thử.Chẳng hạn nhƣ kịch bản “Xem trang chủ” ta có thể chia ra các kịch bản nhƣ là: Xem banner, Xem hệ thống điều hƣớng (navigation), Xem Footer và kịch bản kiểm tra các khối nội dung khác. Các kịch bản đƣợc mô tả nhƣ sau:
Scenario: View banner
Then I should see "SKY COMPUTER" Scenario: View Navigation
Then I should see main menu Scenario: View footer
Then I should see "WEBSITE SKY COMPUTER - DESIGN BY GAMPT" Scenario Outline: View content as expected
Then I should see "<block>" block on the "<region>" region Examples: |block |region | |sub_menu|left | |search_dm |left | |slideshow | center| |sptieubieu | center| |dang_nhap |right | |search_gia |right | |thong_ke | right|
Sau khi viết tính năng, sử dụng Behat để kiểm thử tính năng. Cách gọi lệnh behat nhƣ sau:
bin/behat features/homepage.feature
Ở lƣợt gọi lệnh behat đầu tiên, ta có kết quả nhƣ sau:
10 scenarios (10 undefined) 20 steps (20 undefined) 0m0.165s
You can implement step definitions for undefined steps with these snippets:
/**
* @Given /^I am on homepage$/ */
public function iAmOnHomepage() {
throw new PendingException(); }
/**
* @Then /^I should see "([^"]*)"$/ */
public function iShouldSee($arg1) {
throw new PendingException(); }
/**
* @Then /^I should see main menu$/ */
public function iShouldSeeMainMenu() {
throw new PendingException(); }
/**
* @Then /^I should see "([^"]*)" block on the "([^"]*)" region$/
*/
public function iShouldSeeBlockOnTheRegion($arg1, $arg2) {
throw new PendingException(); }
Công cu ̣ Behat hỗ trợ kiểm thƣ̉ bằng cách đo ̣c kết quả thƣ̣c hiê ̣n ƣ́ng du ̣ng và đặc tả tính năng bằng Gherkin. Để kiểm thƣ̉ ƣ́ng du ̣ng web thì Behat phải đo ̣c đƣợc các kết quả trả ra của trình duyê ̣t web thông qua các bô ̣ mô phỏng trình duyê ̣t. Lần gọi đầu tiên, Behat thông báo tất cả các bƣớc đều ở trạng thái
“undefined” bởi vì nó không tìm thấy các định nghĩa bƣớc tƣơng ứng với các bƣớc trong kịch bản. Trƣờng hợp này Behat sẽ sinh ra một các khung định nghĩa bƣớc còn thiếu để ngƣời dùng có thể viết các định nghĩa bƣớc phù hợp. Nhƣ đã mô tả ở mục 3.4, MinkContext đã cài đặt sẵn một số định nghĩa bƣớc phổ biến với các hành vi của trang web, do đó để giảm thiểu công việc định nghĩa bƣớc, lớp ngữ cảnh FeatureContext của ứng dụng nên đƣợc kế thừa từ lớp MinkContext. Bổ sung đoạn mã extendsMinkContext cho lớp ngữ cảnh nhƣ sau:
/**
* Features context. */
class FeatureContext extends MinkContext {
/**
* Initializes context.
* Every scenario gets it's own context object. *
* @param array $parameters context parameters (set them up through behat.yml)
*/
public function __construct(array $parameters) {
// Initialize your context here }
}
Áp dụng Behat để kiểm thử lại tính năng, ta có kết quả gọi lệnh behat lƣợt thứ hai là:
10 scenarios (8 undefined, 2 failed)
20 steps (10 passed, 8 undefined, 2 failed) 0m0.496s
You can implement step definitions for undefined steps with these snippets:
/**
* @Then /^I should see main menu$/ */
public function iShouldSeeMainMenu() {
throw new PendingException(); }
/**
* @Then /^I should see "([^"]*)" block on the "([^"]*)" region$/
public function iShouldSeeBlockOnTheRegion($arg1, $arg2) {
throw new PendingException(); }
Ở lƣợt gọi lệnh behat lần thứ hai, sau khi lớp ngữ cảnh thừa kế từ MinkContext, ta cần cài đặt hai định nghĩa bƣớc cho kịch bản. Dựa vào khung định nghĩa bƣớc Behat sinh ra, chúng ta sẽ bổ sung các định nghĩa bƣớc còn thiếu cho lớp ngữ cảnh FeatureContext. Các định nghĩa bƣớc đƣợc viết nhƣ sau:
/**
* @Then /^I should see main menu$/ */
public function iShouldSeeMainMenu() {
$session = $this->getSession(); // đọc session từ bộ điều khiển trình duyệt Mink
$mainmenu = $session->getPage()->findAll('css','div#main_menu span a');
if (empty($mainmenu)) {
throw new Exception("No main menu was found on the page"); }
/**
* @Then /^I should see "([^"]*)" block on the "([^"]*)" region$/ */
public function iShouldSeeOnThe($block, $region) {
$session = $this->getSession();
$regione = $session->getPage()->find('css',"div#{$region}"); if (empty($regione)) {
$url = $session->getCurrentUrl();
throw new \Exception(sprintf("No region '%s' on the page %s", $region, $url));
}
if(!$regione->find('css',"div#{$block}")) {
throw new \Exception(sprintf("No block '%s' on the region '%s'", $block,$region));
} }
Sau khi hoàn thiện các định nghĩa bƣớc, gọi lệnh behat lần thứ ba để kiểm thử tính năng, kết quả nhƣ sau:
Background: # features/homepage.feature:6 Given I am on homepage # FeatureContext::iAmOnHomepage()
Then I should see "SKY COMPUTER" #
FeatureContext::assertPageContainsText() The current node list is empty.
Scenario: View Navigation # features/homepage.feature:10 Then I should see main menu #
FeatureContext::iShouldSeeMainMenu() No main menu was found on the page
Scenario: View footer # features/homepage.feature:12 Then I should see "WEBSITE SKY COMPUTER - DESIGN BY GAMPT" #
FeatureContext::assertPageContainsText() The current node list is empty.
Scenario Outline: View content as expected # features/homepage.feature:14
Then I should see "<block>" block on the "<region>" region # FeatureContext::iShouldSeeOnThe()
Examples:
| block | region | | sub_menu | left |
No region 'left' on the page http://localhost/behatdemo/ | search_dm | left |
No region 'left' on the page http://localhost/behatdemo/ | slideshow | center |
No region 'center' on the page ttp://localhost/behatdemo/ | sptieubieu | center |
No region 'center' on the page http://localhost/behatdemo/ | dang_nhap | right |
No region 'right' on the page http://localhost/behatdemo/ | search_gia | right |
No region 'right' on the page http://localhost/behatdemo/ | thong_ke | right |
No region 'right' on the page http://localhost/behatdemo/ 10 scenarios (10 failed)
20 steps (10 passed, 10 failed) 0m0.262s
Kết quả lƣợt kiểm thƣ̉ thứ ba, Behat xác định các phƣơng thức kiểm thử tƣơng ứng với các bƣớc và thống báo trạng thái của từng bƣớc thông qua màu sắc và ngoại lệ mà phƣơng thức kiểm thử trả ra . Các bƣớc thất bại đƣợc đánh dấu màu đỏ, kèm với thống báo lỗi (ngoại lệ) của phƣơng thức kiểm thử tƣơng ứng, các bƣớc thành công có màu xanh lá cây, … . Dựa vào kết quả Behat đƣa ra, ta sẽ cài đặt mã nguồn tƣơng ứng cho các bƣớc chƣa thành công. Tài liệu đặc tả bƣớc và nội dung phƣơng thức kiểm thử của bƣớc là các hƣớng dẫn cần thiết
để cài đặt tính năng. Chẳng hạn với tính năng trên, ta cần viết trang HTML, có các văn bản và các khối tƣơng ứng với đặc tả tính năng. Trang chủ index.php có mã HTML nhƣ sau:
<body>
<div id="body"><!--BEGIN #body-->
<div id="content" class="container_12"> <div id="top"><!--BEGIN #top-->
<div id="logo" class="grid_2">
<a title="Website Sky Computer">Logo</a> </div>
<div id="banner" class="grid_9"> <h1>SKY COMPUTER</h1>
</div>
</div><!--END #top-->
<div id="main_menu" class="grid_12" align="center"> <?php
include_once("include/main_menu.php"); ?>
</div>
<div id="container" class="grid_12"> <div id="left" class="grid_2">
<div id="sub_menu"> </div> <div id="search_dm"> <?php include_once("../include/search_dm.php"); ?> </div> </div>
<div id="center" class="grid_7"> <div id="slideshow">
</div>
<div id="sptieubieu">center </div>
</div>
<div id="right" class="grid_2">Right <div id="dang_nhap"> </div> <div id="search_gia"> </div> <div id="thong_ke"> </div> </div> </div>
<div id="bottom" class="grid_12" align="center"> <div id="copyright"><!--BEGIN #copyright-->
</div><!--END #copyright--> <div id="add"><!--BEGIN #add--> </div>
</div> </div> </body>
Gọi Behat để kiểm thử trang chủ lƣợt thứ tƣ, ta có kết quả:
10 scenarios (10 passed) 20 steps (20 passed) 0m0.321s
Ở lƣợt kiểm thử thứ tƣ, tất cả các kịch bản của tính năng trang chủ đều ở trạng thái thành công, quá trình cài đặt hoàn tất.
Cùng một tính năng, mã cài đặt có thể khác nhau phụ thuộc vào phong cách lập trình của lập trình viên. Phần mã nguồn sau khi cài đặt hoàn thiện cũng có thể có sự tinh chỉnh để mã nguồn mạch lạc, dễ đọc.
4.3.2. Tính năng “Đăng ký thành viên”
Tính năng đăng ký cho phép n gƣời dùng trở thành thành viên của hê ̣ thống bằng cách cung cấp các thông tin hợp lê ̣ thông qua form đăng ký . Tính năng này đƣợc viết dƣới da ̣ng câu chuyê ̣n ngƣời dùng nhƣ sau:
Feature: Register an account
In order to start using additional features of the site As any user
I should be able to register on the site
Các kịch bản đều có chung bƣớc ngữ cảnh là đi đến trang đăng ký từ liên kết “Đăng ký” ở trang chủ. Khai báo kịch bản nền nhƣ sau:
Background:
Given I am on homepage
Kịch bản một: Kiểm tra các thành phần trên Form đăng ký: Form đăng ký chƣ́a các nhãn và các trƣờng nhâ ̣p theo mô tả của ngƣời dùng . Bao gồm Ho ̣ tên, Email, Tên đăng nhâ ̣p, Mâ ̣t khẩu, Nhắc la ̣i mâ ̣t khẩu , Đi ̣a chỉ, Điê ̣n thoại, Ngày sinh, Giới tính. Mỗi nhãn có mô ̣t trƣờng nhâ ̣p với kiểu dƣ̃ liê ̣u phù hợp . Kịch bản cho các thành phần của Form Đăng ký đƣợc viết nhƣ sau:
Scenario: Kiểm tra các thành phần của form đăng ký When I follow "Đăng ký"
Then I should see "Họ và tên"
And I should see input "hoten" with "text" type And I should see "Email"
And I should see "Tên đăng nhập"
And I should see input "tendn" with "text" type And I should see "Mật khẩu"
And I should see input "matkhau" with "password" type And I should see "Nhắc lại mật khẩu"
And I should see input "matkhau2" with "password" type And I should see "Avatar"
And I should see input "avatar" with "file" type And I should see "Ngày sinh"
Sau khi xác đi ̣nh ki ̣ch bản trên , ta go ̣i lê ̣nh behat để kiểm t ra tra ̣ng thái của