Công Cụ Tạo Kịch Bản Kiểm Thử Tự Động Web Từ Quy Trình Thủ Công Với LangChain

Trong quy trình phát triển phần mềm truyền thống, việc tạo các kịch bản kiểm thử tự động cho ứng dụng web thường đòi hỏi kỹ sư kiểm thử phải chuyển đổi thủ công từ các kịch bản kiểm thử chức năng. Các công cụ tạo kịch bản tự động hiện có trên thị trường phần lớn dựa vào phương pháp ghi lại hành động của người dùng để tạo ra các bước kiểm thử. Mặc dù tiện lợi, nhưng các kịch bản được ghi lại thường thiếu tính linh hoạt, khó bảo trì và vẫn cần sự can thiệp thủ công đáng kể.

Với sự ra đời của các Mô hình Ngôn ngữ Lớn (LLM), một hướng tiếp cận mới đã mở ra: liệu LLM có thể thực thi các kịch bản kiểm thử chức năng và sinh ra các kịch bản kiểm thử tự động không?

Giá Trị Ứng Dụng

Khi các kỹ sư kiểm thử mô tả rõ ràng các bước trong kịch bản kiểm thử chức năng, một công cụ sử dụng LLM có thể trực tiếp chuyển đổi chúng thành các kịch bản kiểm thử tự động cho web. Điều này hứa hẹn giảm thiểu đáng kể công sức và tài nguyên cần thiết cho việc tạo và duy trì bộ kiểm thử tự động.

Triển Khai Thực Tế

Nguyên Lý Hoạt Động

Nguyên lý cốt lõi của giải pháp này xoay quanh việc cho phép một LLM tương tác với trình duyệt web thông qua một bộ công cụ chuyên dụng. LLM sẽ phân tích các bước kiểm thử thủ công, sau đó sử dụng các công cụ này để mô phỏng hành vi người dùng, ghi lại các thao tác, và cuối cùng, chuyển đổi các thao tác đã ghi thành mã kiểm thử tự động.

Yêu Cầu Đối Với Kịch Bản Kiểm Thử Thủ Công

Để LLM có thể hiểu và chuyển đổi thành công kịch bản kiểm thử chức năng sang dạng tự động, các bước trong kịch bản phải được diễn đạt một cách rõ ràng và chi tiết. Nếu các bước không rõ ràng hoặc mơ hồ, LLM sẽ gặp khó khăn trong việc xác định các hành động cụ thể cần thực hiện.

Ví dụ về một kịch bản kiểm thử đăng nhập rõ ràng:


1. Mở trang web tại: https://litemall.hogwarts.ceshiren.com/#/login?redirect=%2Fdashboard
2. Nhập "hogwarts" vào trường tên người dùng.
3. Nhập "test12345" vào trường mật khẩu.
4. Nhấn nút "Đăng nhập".
5. Kiểm tra URL hiện tại sau khi đăng nhập thành công.
6. Hoàn thành và đóng trình duyệt.
    

Sử Dụng Agent để Thực Thi Kịch Bản Kiểm Thử

Bản thân các LLM chỉ có khả năng "suy nghĩ" chứ không thể trực tiếp thực hiện các hành động trên trình duyệt. Tuy nhiên, thông qua cơ chế Agent của LangChain, chúng ta có thể "kết nối" các công cụ bên ngoài vào LLM. Để thực thi các bước kiểm thử chức năng, LLM cần được trang bị các công cụ để tương tác với trình duyệt web.

Các khái niệm chính: Agent, Tools (Công cụ).

  • Xây dựng bộ công cụ tương tác web:

    Chúng ta sẽ tạo một lớp Python để gói gọn các thao tác cơ bản của Selenium WebDriver, giúp LLM có thể "điều khiển" trình duyệt.

    
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from time import sleep
    
    class BrowserAutomationClient:
        def __init__(self):
            self._driver = None
            self._active_element = None
    
        def _initialize_webdriver(self):
            if not self._driver:
                self._driver = webdriver.Chrome()
                self._driver.implicitly_wait(5) # Chờ ẩn 5 giây
    
        def navigate_to_url(self, target_url: str) -> str:
            self._initialize_webdriver()
            self._driver.get(target_url)
            return self.get_page_interactive_summary()
    
        def get_page_interactive_summary(self) -> str:
            # Lấy một bản tóm tắt các phần tử tương tác trên trang
            script = """
            var elements_info = "";
            document.querySelectorAll('button, input, a, [role="button"], [type="submit"]').forEach(el => {
                let info = el.outerHTML;
                if (el.placeholder) info += ` placeholder='${el.placeholder}'`;
                if (el.ariaLabel) info += ` aria-label='${el.ariaLabel}'`;
                if (el.id) info += ` id='${el.id}'`;
                if (el.name) info += ` name='${el.name}'`;
                elements_info += info + "\\n";
            });
            return elements_info;
            """
            return self._driver.execute_script(script)
    
        def perform_click_on_element(self) -> str:
            # Nhấp vào phần tử đang được chọn
            if self._active_element:
                self._active_element.click()
                sleep(1) # Chờ một chút sau khi nhấp
            return self.get_page_interactive_summary()
    
        def send_input_text(self, text_to_send: str) -> str:
            # Nhập văn bản vào phần tử đang được chọn
            if self._active_element:
                self._active_element.clear()
                self._active_element.send_keys(text_to_send)
            return self.get_page_interactive_summary()
    
        def locate_element_by_css(self, css_selector: str) -> str:
            # Tìm kiếm và lưu trữ phần tử bằng CSS selector
            print(f"Searching for CSS selector: {css_selector}")
            element = self._driver.find_element(by=By.CSS_SELECTOR, value=css_selector)
            self._active_element = element
            return self.get_page_interactive_summary()
    
        def close_browser_session(self):
            if self._driver:
                self._driver.quit()
                self._driver = None
    
        def retrieve_current_page_url(self) -> str:
            return self._driver.current_url
    
                
  • Tạo công cụ LangChain:

    Sử dụng decorator @tool của LangChain để biến các phương thức của lớp BrowserAutomationClient thành các công cụ mà Agent có thể sử dụng.

    
    from langchain.tools import tool
    import time # Import time for sleep
    
    browser_handler = BrowserAutomationClient()
    
    @tool
    def open_url(url: str) -> str:
        """Mở một URL trong trình duyệt và trả về nội dung tóm tắt của trang."""
        return browser_handler.navigate_to_url(url)
    
    @tool
    def find_element(css_selector: str) -> str:
        """Định vị một phần tử web bằng CSS selector và trả về tóm tắt các phần tử trên trang."""
        return browser_handler.locate_element_by_css(css_selector)
    
    @tool
    def click_element_by_css(css_selector: str) -> str:
        """Định vị phần tử bằng CSS selector rồi nhấp vào nó. Trả về tóm tắt trang."""
        browser_handler.locate_element_by_css(css_selector)
        return browser_handler.perform_click_on_element()
    
    @tool
    def type_text(css_selector: str, text: str) -> str:
        """Định vị phần tử bằng CSS selector và nhập văn bản vào. Trả về tóm tắt trang."""
        browser_handler.locate_element_by_css(css_selector)
        return browser_handler.send_input_text(text)
    
    @tool
    def wait_for_seconds(seconds: int):
        """Chờ một số giây nhất định."""
        time.sleep(seconds)
    
    @tool
    def get_current_browser_url() -> str:
        """Lấy URL hiện tại của trình duyệt."""
        return browser_handler.retrieve_current_page_url()
    
    @tool
    def quit_browser():
        """Đóng trình duyệt."""
        browser_handler.close_browser_session()
    
    automation_tools = [open_url, find_element, click_element_by_css, type_text, wait_for_seconds, get_current_browser_url, quit_browser]
    
                
  • Khởi tạo và chạy Agent:

    Sau khi có các công cụ, chúng ta khởi tạo một Agent và Agent Executor với LLM và bộ công cụ đã tạo.

    
    from langchain import hub
    from langchain.agents import create_structured_chat_agent, AgentExecutor
    from langchain_openai import ChatOpenAI # Ví dụ dùng OpenAI LLM
    from langchain_core.agents import AgentAction
    # ... (các import khác)
    
    llm_model = ChatOpenAI(temperature=0) # Chọn một LLM thích hợp
    
    # Lấy prompt agent từ hub của LangChain
    agent_prompt = hub.pull("hwchase17/structured-chat-agent")
    
    # Tạo Agent
    web_automation_agent = create_structured_chat_agent(llm_model, automation_tools, agent_prompt)
    
    # Tạo Agent Executor
    agent_runner = AgentExecutor(
        agent=web_automation_agent,
        tools=automation_tools,
        verbose=True, # Hiển thị chi tiết quá trình thực thi
        return_intermediate_steps=True, # Trả về các bước trung gian
        handle_parsing_errors=True
    )
                
  • Thực thi Agent:
    
    user_query = """
        Bạn là một chuyên gia kiểm thử tự động. Hãy thực hiện các bước sau trên trình duyệt web,
        mỗi bước định vị phần tử cần dựa trên thông tin HTML trả về từ bước trước đó:
        1. Mở https://litemall.hogwarts.ceshiren.com/#/login?redirect=%2Fdashboard
        2. Nhập 'hogwarts' vào trường tên người dùng.
        3. Nhập 'test12345' vào trường mật khẩu.
        4. Nhấn nút đăng nhập.
        5. Khi vào trang chính, lấy URL hiện tại.
        6. Hoàn thành và đóng trình duyệt.
    """
    execution_result = agent_runner.invoke({"input": user_query})
                

Ghi Lại Các Bước Thực Thi

Agent Executor có khả năng ghi lại tất cả các bước đã thực hiện. Thông tin này được lưu trữ trong thuộc tính intermediate_steps của kết quả trả về. Chúng ta có thể trích xuất và định dạng lại các bước này để sử dụng cho việc tạo mã kiểm thử.


# Lấy kết quả thực thi từ Agent
execution_result = agent_runner.invoke({"input": user_query})

# Lấy các bước thực thi trung gian
intermediate_steps_log = execution_result["intermediate_steps"]
formatted_steps = []

# Duyệt qua các bước để trích xuất thông tin công cụ và đầu vào
for step in intermediate_steps_log:
    action = step[0] # Action là phần tử đầu tiên trong tuple
    if isinstance(action, AgentAction):
        formatted_steps.append({'tool_used': action.tool, 'tool_arguments': action.tool_input})

# formatted_steps giờ chứa một danh sách các dictionary mô tả hành động
            

Tạo Kịch Bản Kiểm Thử Tự Động

Với các bước thực thi đã được ghi lại (dưới dạng JSON), chúng ta có thể chuyển chúng cho một LLM khác (hoặc cùng một LLM với một prompt khác) để tạo ra mã kiểm thử tự động sử dụng pytestSelenium WebDriver.


from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
import json

test_case_generation_prompt = PromptTemplate.from_template("""
Bạn là một kỹ sư kiểm thử tự động web chuyên nghiệp, với kinh nghiệm sử dụng pytest và Selenium WebDriver.
Dưới đây là một chuỗi các bước kiểm thử tự động được mô tả dưới dạng JSON:

{execution_log}

Dựa trên nhật ký thực thi này, hãy viết một kịch bản kiểm thử pytest hoàn chỉnh,
đảm bảo mã nguồn phải chính xác và tuân thủ các phương pháp hay nhất của Selenium.
Sử dụng các tên hàm và biến phù hợp với mục đích kiểm thử.
""")

# ... (Khởi tạo LLM nếu chưa có)
# llm_model = ChatOpenAI(temperature=0)

# Ví dụ cách sử dụng prompt:
# prompt_input = test_case_generation_prompt.format(
#    execution_log=json.dumps(formatted_steps, indent=2),
#    input="Vui lòng tạo mã kiểm thử tự động tương ứng."
# )
# generated_test_code = llm_model.invoke(prompt_input).content

            

Kết Hợp Agent và Chain trong LangChain

Để tối ưu hóa quy trình, chúng ta có thể kết hợp Agent Executor với các Chain của LangChain, tạo thành một luồng làm việc liền mạch.


import json
from langchain import hub
from langchain.agents import create_structured_chat_agent, AgentExecutor
from langchain.globals import set_debug
from langchain_core.agents import AgentAction
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI

# Giả định automation_tools đã được định nghĩa ở trên
# from .automation_tools import automation_tools # Cần điều chỉnh import nếu code ở file riêng

set_debug(True) # Bật chế độ debug để xem chi tiết

# Khởi tạo LLM
main_llm = ChatOpenAI(temperature=0)

# Lấy prompt mặc định cho Agent từ hub
agent_system_prompt = hub.pull("hwchase17/structured-chat-agent")

# Tạo Agent thực thi các bước trình duyệt
browser_action_agent = create_structured_chat_agent(main_llm, automation_tools, agent_system_prompt)

# Tạo Agent Executor
browser_action_executor = AgentExecutor(
    agent=browser_action_agent,
    tools=automation_tools,
    verbose=True,
    return_intermediate_steps=True,
    handle_parsing_errors=True
)

# Kịch bản kiểm thử thủ công đầu vào
manual_test_scenario = """
    Bạn là một chuyên gia kiểm thử tự động. Hãy thực hiện các bước sau trên trình duyệt web,
    mỗi bước định vị phần tử cần dựa trên thông tin HTML trả về từ bước trước đó:
    1. Mở https://litemall.hogwarts.ceshiren.com/#/login?redirect=%2Fdashboard
    2. Nhập 'hogwarts' vào trường tên người dùng.
    3. Nhập 'test12345' vào trường mật khẩu.
    4. Nhấn nút đăng nhập.
    5. Khi vào trang chính, lấy URL hiện tại.
    6. Hoàn thành và đóng trình duyệt.
"""

# Hàm này sẽ được gọi trong LangChain Chain để thực thi Agent và lấy log
def retrieve_agent_execution_log(_):
    # Thực thi Agent với kịch bản thủ công
    result = browser_action_executor.invoke({"input": manual_test_scenario})
    
    # Trích xuất các bước trung gian
    steps_data = result["intermediate_steps"]
    log_entries = []
    
    for entry in steps_data:
        action_item = entry[0]
        if isinstance(action_item, AgentAction):
            log_entries.append({'tool_name': action_item.tool, 'parameters': action_item.tool_input})
            
    return json.dumps(log_entries, indent=2)

# Prompt để tạo mã kiểm thử tự động từ nhật ký thực thi
code_gen_prompt = PromptTemplate.from_template("""
Bạn là một kỹ sư kiểm thử tự động web chuyên nghiệp, với kinh nghiệm sử dụng pytest và Selenium WebDriver.
Dưới đây là một chuỗi các bước kiểm thử tự động đã được ghi lại, mô tả dưới dạng JSON:

{execution_log}

Dựa trên nhật ký thực thi này, hãy viết một kịch bản kiểm thử pytest hoàn chỉnh và chính xác,
tuân thủ các phương pháp hay nhất của Selenium. Đặt tên hàm kiểm thử là `test_login_scenario`.
Hãy cung cấp mã Python đầy đủ, bao gồm cả các import cần thiết và setup/teardown.
""")

# Xây dựng Chain
full_automation_chain = (
    RunnablePassthrough.assign(execution_log=retrieve_agent_execution_log) # Gọi hàm để lấy log
    | code_gen_prompt # Truyền log vào prompt tạo mã
    | main_llm # Truyền prompt cho LLM
    | StrOutputParser() # Phân tích output thành chuỗi
)

# Chạy Chain để sinh ra mã kiểm thử
print(full_automation_chain.invoke({"input": "Vui lòng tạo mã kiểm thử tự động cho quy trình này."}))
            

Kết Quả Sinh Mã Kiểm Thử

Dưới đây là một ví dụ về mã kiểm thử tự động được sinh ra dựa trên các bước đã thực hiện:


import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

@pytest.fixture(scope="module")
def browser_instance():
    driver = webdriver.Chrome()
    driver.implicitly_wait(10)
    yield driver
    driver.quit()

def test_login_scenario(browser_instance):
    driver = browser_instance
    base_url = "https://litemall.hogwarts.ceshiren.com/#/login?redirect=%2Fdashboard"

    # Bước 1: Mở trang web
    driver.get(base_url)
    WebDriverWait(driver, 10).until(EC.url_to_be(base_url))
    print(f"Navigated to: {driver.current_url}")

    # Bước 2: Nhập tên người dùng
    username_field = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.CSS_SELECTOR, "input[name='username']"))
    )
    username_field.clear()
    username_field.send_keys("hogwarts")
    print("Entered username.")

    # Bước 3: Nhập mật khẩu
    password_field = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.CSS_SELECTOR, "input[name='password']"))
    )
    password_field.clear()
    password_field.send_keys("test12345")
    print("Entered password.")

    # Bước 4: Nhấn nút đăng nhập
    login_button = WebDriverWait(driver, 10).until(
        EC.element_to_be_clickable((By.CSS_SELECTOR, "button[type='button'].el-button--primary span"))
    )
    login_button.click()
    print("Clicked login button.")

    # Bước 5: Kiểm tra URL sau khi đăng nhập
    expected_dashboard_url = "https://litemall.hogwarts.ceshiren.com/#/dashboard" # Giả định URL sau đăng nhập
    WebDriverWait(driver, 20).until(EC.url_contains("/dashboard"))
    current_page_url = driver.current_url
    print(f"Current URL after login: {current_page_url}")
    assert "/dashboard" in current_page_url, "Failed to navigate to dashboard after login."
    print("Successfully verified dashboard navigation.")

    # Bước 6: Đóng trình duyệt (được xử lý bởi fixture)
    

Thẻ: langchain LLM KiểmThửTựĐộng WebAutomation selenium

Đăng vào ngày 2 tháng 6 lúc 19:57