Phân tích & thiết kế

Từ Use Case Thêm sản phẩm đến mã kiểm thử

Viết luồng chính, rẽ nhánh theo ngoại lệ và truy vết từng bước sang hàm nghiệp vụ cùng ca kiểm thử.

Thực hànhTrung cấp12 phút đọcJavaScript · UML

Thích và đánh giá Bình luận và hỏi đáp
Luồng lưu sản phẩm rẽ nhánh khi dữ liệu sai hoặc mã bị trùng, và chỉ xác nhận thành công sau khi lưu.
Luồng lưu sản phẩm rẽ nhánh khi dữ liệu sai hoặc mã bị trùng, và chỉ xác nhận thành công sau khi lưu. Mở hình lớn ↗

1. Mô tả mục tiêu, tác nhân và hậu điều kiện

Nhân viên hoặc quản lý cần đưa một mặt hàng mới vào danh mục. Trước khi thực hiện, người dùng đã đăng nhập và có quyền quản lý sản phẩm. Sau khi hoàn tất, danh mục có đúng một mặt hàng theo mã đã chuẩn hóa và màn hình hiển thị xác nhận.

Bản ví dụ quy định mã dài 3–16 ký tự, gồm chữ cái Latin, chữ số và dấu gạch ngang. Hệ thống cắt khoảng trắng ngoài và chuẩn hóa chữ hoa. Tên không để trống, dài tối đa 100 ký tự. Giá tính bằng đồng, là số nguyên không âm. Đặc tả càng rõ thì ca kiểm thử càng dễ đặt kỳ vọng cụ thể.

2. Tách luồng chính và các nhánh ngoại lệ

  1. Tác nhân chọn chức năng thêm sản phẩm.
  2. Hệ thống mở biểu mẫu mã, tên và giá bán.
  3. Tác nhân nhập dữ liệu và chọn Lưu.
  4. Hệ thống kiểm tra quyền, dữ liệu và mã trùng, sau đó lưu bản ghi.
  5. Hệ thống thông báo thành công và hiển thị thông tin vừa lưu.

Khi mã sai định dạng hoặc giá âm, biểu mẫu giữ lại dữ liệu và đánh dấu trường cần sửa. Khi mã trùng sau chuẩn hóa, hệ thống báo trùng mã và không ghi đè. Khi người dùng thiếu quyền, hệ thống từ chối trước khi ghi. Mỗi ngoại lệ cần xác định rõ phản hồi giao diện và trạng thái dữ liệu sau thao tác.

3. Ánh xạ kịch bản sang mã nghiệp vụ

Hàm addProduct nhận dữ liệu, vai trò và danh mục mô phỏng. Giá trị trả về gồm kết quả hoặc mã lỗi. Giao diện có thể dùng trường field để đặt thông báo gần ô nhập tương ứng.

Môi trường thực hành. Node.js 22 trở lên. Chạy node --test cùng tên tệp; các ca mẫu xử lý dữ liệu trong bộ nhớ.

JavaScript · UML · them-san-pham.test.mjsTải mã nguồn
import assert from "node:assert/strict";
import test from "node:test";

function addProduct(input, role, products) {
  if (role !== "employee" && role !== "manager") {
    return { ok: false, code: "FORBIDDEN" };
  }
  const code = typeof input?.code === "string" ? input.code.trim().toUpperCase() : "";
  const name = typeof input?.name === "string" ? input.name.trim() : "";
  const price = input?.price;

  if (!/^[A-Z0-9-]{3,16}$/.test(code)) {
    return { ok: false, code: "INVALID_CODE", field: "code" };
  }
  if (!name || name.length > 100) {
    return { ok: false, code: "INVALID_NAME", field: "name" };
  }
  if (!Number.isSafeInteger(price) || price < 0) {
    return { ok: false, code: "INVALID_PRICE", field: "price" };
  }
  if (products.has(code)) {
    return { ok: false, code: "DUPLICATE_CODE", field: "code" };
  }

  const product = Object.freeze({ code, name, price });
  products.set(code, product);
  return { ok: true, product };
}

test("lưu và xác nhận với mã đã chuẩn hóa", () => {
  const products = new Map();
  const result = addProduct({ code: " sp01 ", name: " Bút bi ", price: 5000 }, "employee", products);
  assert.deepEqual(result, { ok: true, product: { code: "SP01", name: "Bút bi", price: 5000 } });
  assert.equal(products.size, 1);
  assert.deepEqual(products.get("SP01"), result.product);
});

test("mã trùng không ghi đè sản phẩm đang có", () => {
  const existing = { code: "SP01", name: "Bút bi", price: 5000 };
  const products = new Map([["SP01", existing]]);
  const result = addProduct({ code: "sp01", name: "Bút mới", price: 9000 }, "manager", products);
  assert.deepEqual(result, { ok: false, code: "DUPLICATE_CODE", field: "code" });
  assert.equal(products.size, 1);
  assert.deepEqual(products.get("SP01"), existing);
});

test("giá sai và vai trò sai đều không thay đổi dữ liệu", () => {
  const products = new Map();
  assert.equal(addProduct({ code: "SP02", name: "Vở", price: -1 }, "employee", products).code, "INVALID_PRICE");
  assert.equal(addProduct({ code: "SP02", name: "Vở", price: 8000 }, "viewer", products).code, "FORBIDDEN");
  assert.equal(products.size, 0);
});

Hàm gọi products.set sau các bước kiểm tra. Vì vậy ca thành công xác nhận dữ liệu được ghi, còn ca ngoại lệ xác nhận bản ghi cũ và kích thước danh mục. Mã demo dùng Map trong một tiến trình. Khi dùng cơ sở dữ liệu nhiều người dùng, đặt ràng buộc duy nhất cho mã đã chuẩn hóa và chuyển lỗi trùng của kho dữ liệu về nhánh ngoại lệ tương ứng.

4. Truy vết từ bước sử dụng đến ca kiểm thử

Quy tắc trong Use CaseDữ liệu thửĐiều cần xác nhận
Lưu thành côngMã sp01 có khoảng trắng ngoàiDanh mục chứa đúng một mục mã SP01
Mã phải duy nhấtDanh mục đã có SP01, nhập sp01Báo DUPLICATE_CODE, bản ghi cũ còn nguyên
Giá không âmGiá -1Báo INVALID_PRICE, danh mục không tăng
Chỉ nhân viên và quản lý được ghiVai trò viewerBáo FORBIDDEN

Chạy node --test them-san-pham.test.mjs với Node.js 22 hoặc mới hơn. Ba ca phải đạt. Bổ sung hai trường hợp giá 0 và giá gửi dưới dạng chuỗi. Sau khi viết kỳ vọng cho dữ liệu, thử tiếp ca bấm nút Lưu hai lần trên giao diện để kiểm tra thông báo trùng và hành vi giữ biểu mẫu.