Hướng dẫn AI Agent tự động viết code C# NUnit Selenium Test (Data-Driven) sử dụng dữ liệu từ ExcelDumperTool và UI-Map YAML.
Skill này hướng dẫn AI Agent cách viết thêm Test tự động mới cho dự án C# NUnit + Selenium, dựa trên dữ liệu Excel và file UI-Map. Khi thêm test cho một module mới, Agent CHỈ CẦN tạo 1 file (ví dụ EmployeeCreateTest.cs) và kế thừa từ BaseTest — vì BaseTest.cs và ExcelHelper.cs đã có sẵn trong dự án, không cần tạo lại.
.csproj)<PackageReference Include="NUnit" Version="4.*" />
<PackageReference Include="Selenium.WebDriver" Version="4.*" />
<PackageReference Include="Selenium.Support" Version="4.*" />
<PackageReference Include="EPPlus" Version="7.*" />
<PackageReference Include="NUnit3TestAdapter" Version="4.*" />
using Statements bắt buộc ở đầu mỗi file Test mớiusing System;
using System.IO;
using NUnit.Framework;
using OfficeOpenXml;
using OpenQA.Selenium;
namespace TestProductGroup; // namespace giống project gốc
TestProductGroup/TestProductGroup/<ModuleName>Test.cspublic class XYZTest : BaseTestBaseTest — Bắt buộc đọc examples/BaseTest.csBạn được gọi trực tiếp trong class con:
| Thành phần | Mô tả |
|---|---|
ExcelPath | const — Đường dẫn tuyệt đối tới file Excel |
SaveToNewFile | virtual bool — Mặc định true: tự sao chép file gốc sang file mới có timestamp. Override với false trong class con nếu muốn ghi đè file gốc |
OpenExcelForWrite() | Mở file Excel để ghi kết quả — trả về ExcelPackage. Lần đầu tạo file mới có timestamp, các test class chạy sau dùng chung file đó (static _sharedOutputPath). Dùng thay cho new ExcelPackage(new FileInfo(ExcelPath)) |
UrlLogin, UrlCategories | const — URL các trang web |
Username, Password | Đọc từ biến môi trường hoặc default |
driver, wait | Protected fields — ChromeDriver + WebDriverWait |
Setup() / TearDown() | [SetUp]/[TearDown] — không override |
Login() | Điều hướng và đăng nhập tự động |
OpenCreateForm() | Mở form /categories/create |
OpenCategories() | Mở trang danh sách /categories |
SetText(id, value) | Clear + SendKeys vào input theo ID |
ClickSubmitButton() | Click nút Lưu / submit |
GetNameError() | Lấy lỗi validation bên dưới input#name |
GetToastMessage() | Lấy thông báo toast (SweetAlert2) |
GetFinalMessage(bool) | Kết hợp: NameError > Toast > "Hợp lệ" |
IsValidTestCaseCode(code) | ⚠️ Xem mục 5 bên dưới |
RequireWorksheet(pkg, keyword, idx) | Mở sheet theo tên/keyword, fallback index |
FindFirstDataRow(sheet) | Bỏ qua tiêu đề, tìm dòng TC đầu tiên |
ResolveTestData(raw) | Làm sạch: "-" → "", bỏ ngoặc kép |
ParseMultiField(raw) | ⚠️ Xem mục 5 bên dưới |
SaveCaseResult(sheet, row, actual, expected) | Ghi PASS/FAIL vào cột 7, 8 |
SavePackageWithRetry(package) | Lưu file Excel, retry nếu file đang mở |
ExcelHelper — Bắt buộc đọc examples/ExcelHelper.csLớp static chứa logic xử lý Excel ở mức thấp. BaseTest chỉ là wrapper gọi tới đây. Agent không cần viết lại.
⛔ Điều Kiện Tiên Quyết — Hỏi Trước Khi Làm
Trước khi thực hiện bất kỳ bước nào, Agent PHẢI kiểm tra và hỏi người dùng nếu chưa được cung cấp:
Đường dẫn file Excel (test data) — nếu chưa có, hỏi:
"Bạn vui lòng cung cấp đường dẫn tuyệt đối đến file Excel chứa test cases (VD:
D:\project\test-data.xlsx)?"File UI-Map YAML (tọa độ các element trên UI) — nếu chưa có, hỏi:
"Bạn vui lòng cung cấp nội dung hoặc đường dẫn file
ui-map.yamlcho module cần viết test?"Không được tự bịa hoặc giả định hai thông tin trên. Nếu thiếu một trong hai, dừng lại và hỏi trước.
Chạy ExcelDumperTool và xuất ra file:
dotnet run --project "<ABSOLUTE_PATH_TO_EXCEL_DUMPER_TOOL>" -- "<ABSOLUTE_PATH_TO_EXCEL_FILE>" all > all_sheets_output.txt
Đọc file all_sheets_output.txt. Mỗi dòng có dạng:
R001: [Cột1-MãTC] [Cột2-Trường] [Cột3] [Cột4] [Cột5-Input] [Cột6-Expected]
Cột 5 = dữ liệu nhập vào, Cột 6 = kết quả kỳ vọng.
Yêu cầu QA cung cấp ui-map.yaml. Tham khảo ui-map-template.yaml để hiểu format.
⚠️ Radix UI / Shadcn UI sinh ID dạng
_r_6_tự động, không ổn định. BẮT BUỘC dùng XPath Text cho combobox://div[@role='option'][contains(text(), 'Giá trị')]
Trước khi sinh code, xác định các điểm cần thay đổi:
OpenCreateForm, OpenFirstEditForm, OpenSearchPage...). Không dùng mẫu của loại khácA-, B-, C-. Module mới có thể khác → sửa regex trong IsValidTestCaseCode (xem mục 5)/categories, cần thêm const và hàm Open...Form() trong class conTạo file tuân thủ cấu trúc:
[NonParallelizable] attribute trên class.BaseTest.ExecuteCase(tcCode, field, rawData) → fill form → trả về string actual.[Test] public void TestCases() → mở Excel bằng OpenExcelForWrite() → vòng lặp đọc Excel → gọi ExecuteCase → SaveCaseResult.Lưu ý: Luôn dùng
OpenExcelForWrite()thay vìnew ExcelPackage(new FileInfo(ExcelPath)). Mặc định sẽ tự sao chép sang file mới có timestamp, giữ nguyên file test data gốc.
| Tình huống | Cách xử lý |
|---|---|
| TC "Quay lại / Hủy bỏ" | Click XPath //a[contains(.,'Quay lại')], trả về string mô tả URL hiện tại |
Dấu - trong Excel | ResolveTestData đã tự chuyển thành "" |
| Cần nhập 2 trường trong 1 TC (multi-field) | Gọi ParseMultiField (xem cảnh báo mục 5) |
Combobox / Dropdown ảo (không phải <select>) | Click trigger → Click option bằng XPath Text (2 bước) |
| Delete với Confirmation Modal | Xem mẫu examples/ProductGroupDeleteTest.cs |
| Edit test — mở form chỉnh sửa | Không điều hướng tới /create. Dùng helper OpenFirstEditForm(): OpenCategories() rồi click //a[contains(@href,'/categories/') and contains(@href,'/edit')]. Xem mẫu examples/ProductGroupEditTest.cs |
fallbackIndex trong RequireWorksheetRequireWorksheet tìm sheet theo tên (keyword) trước — fallbackIndex chỉ là lưới an toàn cuối cùng nếu không tìm thấy sheet theo tên.
⚠️ Không giả định thứ tự sheet. Số lượng và loại test (Create, Edit, Delete, Search, Export, ...) hoàn toàn phụ thuộc vào file Excel của từng dự án.
Nguyên tắc xác định fallbackIndex:
"Tìm kiếm", "Xuất dữ liệu").fallbackIndex.Ví dụ dự án có 4 loại test:
// Create
var sheet = RequireWorksheet(package, "Thêm", fallbackIndex: 0);
// Edit
var sheet = RequireWorksheet(package, "Chỉnh sửa", fallbackIndex: 1);
// Delete
var sheet = RequireWorksheet(package, "Xóa", fallbackIndex: 2);
// Search — loại bổ sung, keyword ưu tiên
var sheet = RequireWorksheet(package, "Tìm kiếm", fallbackIndex: 3);
IsValidTestCaseCode — Regex cứng theo module// BaseTest.cs hiện tại:
Regex.IsMatch(tcCode, @"^[ABC]-\d{2}$") // Chỉ khớp A-01, B-02, C-03...
Nếu module mới dùng mã khác (VD: EMP-01, D-01), bạn PHẢI override hoặc điều chỉnh logic này. Cách đơn giản nhất là đặt điều kiện check rộng hơn trong vòng lặp test của class con.
ParseMultiField — Hardcode cho "Nhóm hàng hóa"// ExcelHelper.cs hiện tại chỉ nhận biết:
line.StartsWith("Tên nhóm hàng hóa") → name
line.StartsWith("Mô tả") → description
Nếu module mới có trường khác (VD: Nhân viên có Họ tên, Ngày sinh, Giới tính, Địa chỉ), hàm này sẽ trả về rỗng. Bạn cần viết hàm parse riêng trong class con hoặc file Helper mới.
Mẫu viết hàm parse riêng:
private (string fullName, string birthDate, string gender, string address) ParseEmployeeData(string raw)
{
string fullName = "", birthDate = "", gender = "", address = "";
if (string.IsNullOrWhiteSpace(raw)) return (fullName, birthDate, gender, address);
var lines = raw.Replace("\r", "").Split('\n');
foreach (var line in lines.Select(x => x.Trim()))
{
if (line.StartsWith("Họ tên", StringComparison.OrdinalIgnoreCase))
fullName = line[(line.IndexOf(':') + 1)..].Trim();
else if (line.StartsWith("Ngày sinh", StringComparison.OrdinalIgnoreCase))
birthDate = line[(line.IndexOf(':') + 1)..].Trim();
else if (line.StartsWith("Giới tính", StringComparison.OrdinalIgnoreCase))
gender = line[(line.IndexOf(':') + 1)..].Trim();
else if (line.StartsWith("Địa chỉ", StringComparison.OrdinalIgnoreCase))
address = line[(line.IndexOf(':') + 1)..].Trim();
}
return (fullName, birthDate, gender, address);
}
| File | Nội dung |
|---|---|
examples/BaseTest.cs | Lớp cơ sở đầy đủ với null-guard, hàm navigation, Excel helpers |
examples/ExcelHelper.cs | Lớp static xử lý parse/ghi Excel |
examples/ProductGroupCreateTest.cs | Test mẫu: Create — vòng lặp Data-Driven cơ bản |
examples/ProductGroupEditTest.cs | Test mẫu: Edit — điều hướng đến record đã có, mở form Edit |
examples/ProductGroupDeleteTest.cs | Test mẫu: Delete — xử lý confirmation modal |
Thứ tự đọc bắt buộc: BaseTest.cs → ExcelHelper.cs → ProductGroupCreateTest.cs → ProductGroupEditTest.cs → ProductGroupDeleteTest.cs → viết file mới.