Testing Flow: Gia tăng hiệu suất làm việc trong automation testing

Testing Flow: Gia tăng hiệu suất làm việc trong automation testing

Xem nhanh

Nếu chúng ta thực hiện việc "ABC" một lần duy nhất thì dù có khó khăn, tốn kém lẫn vất vả cũng không bận tâm nhiều. Nhưng nếu việc đó lặp đi lặp lại nhiều lần thì ắt hẳn ai trong chúng ta cũng tìm cách tối ưu nó.
Trong tự động hóa kiểm thử ở Cybozu cũng vậy: trải nghiệm → tìm kiếm bất tiện → tối ưu hệ thống. Trong bài viết này chúng ta sẽ tìm hiểu test flow - là kỹ thuật được đúc kết của Cybozu và cũng là một trong loạt bài How to build an E2E testing project

Trong bài này sử dụng Address module | Cybozu Garoon (username: brown password: brown) để làm minh họa, ví dụ

Cybozu Garoon là một sản phẩm làm việc cộng tác. Nó là một trong các sản phẩm chủ lực của tập đoàn Cybozu. Bạn có thể thử trải nghiệm online để biết thêm về sản phẩm

Test flow là gì?

Là một kỹ thuật triển khai test code ở Cybozu mà trong đó test flow class cung cấp test flow methods cho test spec sử dụng nó mà không quan tâm test flow methods được triển khai như thế nào. Kỹ thuật này giúp cho việc triển khai test spec được nhẹ nhàng hiệu quả.

test flow methods đảm nhiệm nhiệm vụ gọi actions method trên một hoặc nhiều trang

Ví dụ: test flow method:

Copy
loginGaroonSystem(){ 
  // ...
}

gọi các "actions method" tác trên web element như: inputUsername(this.userName), inputPassword(this.password), click vào nút “Login” - clickLoginButton().

Test flow giải quyết vấn đề gì?

Để trả lời cho câu hỏi này chúng ta xem qua một số điểm chính bên dưới:

Giúp test spec chỉ thể hiện tổng quát

Tại tầng test spec, khi implement hay maintain chúng ta chỉ đối diện với những test flow methods với tên đại diện phản ánh chi tiết kịch bản nó đảm nhiệm. Điều này giúp chúng ta nắm bắt nhanh và hiệu quả vì test code ít nhất; đồng thời dễ dàng nhận ra sự dư thừa hay thiếu xót của implement test case, ngoài ra khi cần chúng ta sẽ đi vào chi tiết test flow methods. Ví von một tí giống như chúng ta tìm một chiếc điện thoại bỏ quên trong căn phòng ít đồ và một căn phòng nhiều đồ vậy ;)

Ví dụ bên dưới mô tả SỬ DỤNG test flow và KHÔNG sử dụng test flow.

Copy
import { LoginPage } from '#e2e-core/system/pages';
import  { AddingAddressBookFlow } from '#e2e-core/address/test-flows';

describe('SharedAddress thêm mới', () => {
  it('sẽ thêm mới một AddressBook thành công',() => {
      // Đăng nhập vào hệ thống sử dụng trực tiếp Page Object
      LoginPage
        .openUrl('https://onlinedemo2.cybozu.info/scripts/garoon/grn.exe/index?')
        .inputUsernameTextbox("user-1")
        .inputPasswordTextbox("123456")
        .clickLoginButton()
        .getDisplayUserInfo()
        .verifyDisplayUserInfo();

      // Thêm mới một adddressBook vào module Address sử dụng kỹ thuật Test flow
      new AddingAddressBookFlow(addressBookInfo) // Test flow class tên AddingAddressBookFlow
          .addBook()                             // Test flow method tên addBook
          .verifyBookDetails();                  // Test flow method tên verifyBookDetails
  });  
});

Code snippet 1.0: test spec dòng 7-13 dùng trực tiếp page object; dòng 17-19 dùng kỹ thuật test flow

Nếu một test case phức tạp hơn tí, có nhiều bước hơn thì lúc này test spec sẽ bùng nổ số dòng; hoặc khi làm việc test spec ta cũng "không quan tâm lắm" việc đăng nhập gồm những bước gì. Như vậy test flow giải quyết vấn đề ẩn đi sự chi tiết chỉ quan tâm tới tổng thể ở tầng test spec.

Gia tăng hiệu suất làm việc thông qua tái sử dụng test flow

Việc lặp đi lặp lại các action methods của page objecttest spec một cách nhỏ lẻ thật sự tốn cost và không giúp chúng ta làm việc hiệu quả trong automation test project.
Vì End-to-end testing đâu chỉ có một hoặc vài test case. Đôi khi lên đến hàng trăm thậm chí hàng ngàn. Mỗi test case trong test suite thường có chung một hoặc vài nghiệp vụ (ví dụ đăng nhập). Nên việc tái sử dụng test flow giúp chúng ta implement nhiều test case hơn trong cùng một khoản thời gian.

Ví dụ bên dưới ta sử dụng test flow methods new LoggingSystem(accountNormal).loggin(); cho đăng nhập thay vì dùng Page object trực tiếp với nhiều action methods.

Copy
import { LogingSystemFlow } from '#e2e-core/system/test-flows';
import { AddingAddressFlow, OpeningAddressEntryFlow, DeletingAddressBookFlow } from '#e2e-core/address/test-flows';

import AddressBookPage from '#e2e-core/address/pages/AddressBook';

describe('SharedAddress thêm mới', () => {
    it('sẽ thêm mới một AddressBook thành công', () => {
        // Login hệ thống dùng test flow
        new LogingSystemFlow(accountNormal).loggin();
        // Test flow
        new AddingAddressBookFlow(addressBookInfo)
            .addBook()
            .verifyBookDetails();
    });

    it('sẽ thêm mới một AddressBookProxy thành công', () => {
        // Login hệ thống dùng test flow
        new LogingSystemFlow(accountProxy).loggin();
        
        // Thêm mới một address
        new AddingAddressEntryFlow(addressBookProxyInfo)
            .addAddressEntry()
            .verifyAddressEntryDetails();
    });

    after('Xóa tất cả test data đã phát sinh ở test case', () => {
        new LogingSystemFlow(accountProxy).loggin();
        new OpeningAddressEntryFlow(bookName).openDetailAddressBook();

        // Sử dụng trực tiếp action method của page object
        AddressBookPage.waitForPageReady();

        new DeletingAddressBook(bookName).deleteAddressBook();
    });
});

Như vậy càng có nhiều test spec triển khai thì test flow càng phát huy tác dụng. Đến đây test flow giải quyết vấn đề thứ 2 là hiệu suất của developer thấp nếu gọi trực tiếp action methods từ page object.

Cải thiện test code thành test flow như thế nào?

Ví dụ cải thiện test code cho "đăng nhập"

Chuyển các action methods của LoginPage object thành test flow LoggingSystem class và bao gồm login test flow method cho test spec sử dụng.

Test flow class

Test flow class có constructor để nhận dữ liệu và gán vào property this._account cho tất cả test flow method sử dụng nếu có nhu cầu.

Như code snippet bên dưới chúng ta có this._account được khởi tạo ở constructor;

Copy
import LoginPage from './login.js';
import IndexPage from './index.js';

export default class LogingSystem {
    constructor(account) {
        this._account = account;
    }
// ...
}
Implement test flow methods

Tương tác trên một hoặc nhiều trang để triển khai nghiệp vụ mà test flow đảm nhiệm. Test flow methods phần lớn return this; tương tự như action methods của Page Object. Ví dụ login methods return this dòng số 11 bên dưới

Copy
/**
 *
 * @returns {LoggingSystem}
 */
login() {
    LoginPage.openPage()
        .inputUsername(this._account.userName)
        .inputPassword(this._account.userName)
        .clickLoginButton();

    return this;
}

/**
 *
 * @returns {LoggingSystem}
 */
verifyLoggedInSuccessful() {
    const actualLoginName =
        IndexPage.waitForPageReady().getDisplayUserInfo();

    assert.equal(this._account.userName, actualLoginName, `Mong đợi ${this._account.userName} không thể là ${actualLoginName}`);

    return this;
}
Kết quả test flow cuối cùng

Bên dưới là kết quả cuối cùng gồm constructor, flow methods.

Copy
import LoginPage from './login.js';
import IndexPage from './index.js';

export default class LogingSystem {
    constructor(account) {
        this._account = account;
    }

    /**
     *
     * @returns {LoggingSystem}
     */
    login() {
        LoginPage
            .openPage()
            .inputUsername(this._account.userName)
            .inputPassword(this._account.userName)
            .clickLoginButton();

        return this;
    }

    /**
     *
     * @returns {LoggingSystem}
     */
    verifyLoggedInSuccessful() {
        const actualLoginName =
            IndexPage
            .waitForPageReady()
            .getDisplayUserInfo();

        assert.equal(this._account.userName, actualLoginName, `Mong đợi ${this._account.userName} không thể là ${actualLoginName}`);

        return this;
    }
}

Một số điểm lưu ý

  1. Trong test flow class chỉ implement những test flow method liên quan "mật thiết" với test flow class.

    • Ví dụ adding-appointment.js file có AddingAppointment class bao gồm addRegularAppointment methods; addRepeatingAppointment method; addAllDayAppointment method; NHƯNG không implement deleteAppointment method trong AddingAppointment class;
    • Thiết kế test flow nên áp dụng nguyên lý “tính đơn nhiệm”. Không nên “kiêm nhiệm” trong một test flow ví dụ như loginAndAddUser(), addAndDeleteUser(). Điều này sẽ giúp ích cho việc tái sử dụng một cách linh động, dễ bảo trì và dễ cải tiến source code.
      Chúng ta có thể hiểu như: chỉ có một lý do để test flow tồn tại và cũng chỉ có một lý do để test flow mất đi
  2. Tên file và tên class bắt đầu là một "danh động từ" (trong tiếng Anh là v+ing). Vì chúng ta đang đối tượng hóa hành động trong kiểm thử; ví dụ logging.jsLogging class;

  3. Có thể sử dụng action methods ở test spec
    Test flows methods không thể phủ toàn bộ được tất cả test case. Do đó chúng ta có thể triệu gọi action methods của page object ở test spec (nhưng hạn chế nhất có thể)

  4. Mỗi test flow nên hoàn chỉnh về nghiệp vụ
    Một test flow nên được thiết kế để thực hiện một tính năng hoặc nghiệp vụ hoàn chỉnh và độc lập. Ví dụ test flow login() gồm hai phương thức: inputUsername(), inputPassword(). Test flow này vẫn chưa hoàn thành việc login (nên có thêm clickLoginButton() nên khi dùng developer phải viết thêm các phương thức còn lại dẫn đến không phát huy được lợi ích của test flow.

  5. Chỉ nên tạo mới một test flow khi nó có khả năng tái sử dụng.
    Như đã giới thiệu ở trên, lợi ích chính của việc phát sinh test flow là tái sử dụng, nên những test flow được dùng ít (một hoặc hai lần) thì không nhất thiết được tạo ra.

Phân bổ test flow trong cấu trúc dự án

Trong dự án test automation; test flow được phân bổ dưới test-flows folder. Xem thêm ví dụ bên dưới cho hai test flow adding-address-entry.jsopening-address-entry.js

Copy
acceptance
|---address
|   |---test-flows
|   |   |---adding-address-entry.js
|   |   |---opening-address-entry.js
|   |   |---// ...
|   |   |---index.js
|   |---test-specs
|   |---page-objects
|---schedule
|   |---test-flows
|   |   |---adding-appointment.js  // test flow file
|   |   |---// ...
|   |   |---index.js
|   |---test-specs
|   |   |---added-new-appoiment
|   |   |   |---added-new-appointment.spec.js|.data.js
|---system
|   |---test-flows
|   |   |   |---logging.js // test flow file
// ...

Những nhược điểm khi sử dụng test flows

Xét về ưu điểm khi sử dụng test flow, lợi ích đã liệt kê trên. Ngoài những ưu điểm của test flow thì nó cũng có những khuyết điểm:

  • Cấu trúc chương trình sẽ trở nên phức tạp hơn. Xét về quy trình hoạt động từ lúc khởi tạo để thực thi test spec đến lúc chương trình tương tác trên browser sẽ thông qua 4 tầng thay vì 3 tầng nếu không sử dụng test flow (xem Hình 01)

    Hình 01: Chương trình thực thi khi có thêm test flow

    Hình 01: Mô tả chương trình thực thi khi có thêm test flow

  • Tăng gánh nặng cho developer vì phải nhớ và hiểu kỹ lưỡng từ nguyên lý đến định nghĩa. Nếu tuân thủ nửa vời sẽ dẫn đến nhiều biến thể trong chương trình.

Các bài viết cùng chủ đề