From add4acff886df759f1da208ebdb4fb0d13241ac2 Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 11 Jun 2025 22:15:09 -0500 Subject: [PATCH 1/2] refactor: reorder imports and enhance test data setup in product service tests --- src/lib/utils.tests.ts | 4 +- src/services/product.service.test.ts | 112 +++++++++++++++++---------- 2 files changed, 72 insertions(+), 44 deletions(-) diff --git a/src/lib/utils.tests.ts b/src/lib/utils.tests.ts index c420c04..9f1dafd 100644 --- a/src/lib/utils.tests.ts +++ b/src/lib/utils.tests.ts @@ -1,6 +1,8 @@ +import { vi } from "vitest"; + import type { User } from "@/models/user.model"; + import type { Session } from "react-router"; -import { vi } from "vitest"; type TestRequestConfig = { url?: string; diff --git a/src/services/product.service.test.ts b/src/services/product.service.test.ts index 57cd8ba..7ee2fda 100644 --- a/src/services/product.service.test.ts +++ b/src/services/product.service.test.ts @@ -1,16 +1,30 @@ -import { describe, it, vi, beforeEach, expect } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import type { Category } from "@/models/category.model"; import type { Product } from "@/models/product.model"; import * as productRepository from "@/repositories/product.repository"; import { getCategoryBySlug } from "./category.service"; -import { getProductsByCategorySlug } from "./product.service"; +import { getProductById, getProductsByCategorySlug } from "./product.service"; // Mock dependencies vi.mock("@/repositories/product.repository"); vi.mock("./category.service"); +// Test data setup +const mockCategory: Partial = { + id: 1, + slug: "polos", +}; + +const mockProduct: Partial = { + id: 1, + title: "Test Product", + price: 100, + categoryId: mockCategory.id, + description: "Test description", +}; + describe("Product Service", () => { beforeEach(() => { vi.clearAllMocks(); @@ -18,69 +32,81 @@ describe("Product Service", () => { describe("getProductsByCategorySlug", () => { it("should return products for a valid category slug", async () => { - // 1. Mock getCategoryBySlug to return a valid category with id - const mockCategory = { - id: 1, - slug: "polos", - } as Category; - - vi.mocked(getCategoryBySlug).mockResolvedValue(mockCategory); - - // 2. Mock productRepository.getProductsByCategory to return array of products + // Step 1: Setup - Create test data with valid category and products const mockedProducts = [ - { id: 1, title: "Product 1", price: 10, categoryId: 1 } as Product, - { id: 2, title: "Product 2", price: 20, categoryId: 1 } as Product, + { ...mockProduct, id: 1 }, + { ...mockProduct, id: 2, title: "Test Product 2" }, ]; + // Step 2: Mock - Configure repository responses + vi.mocked(getCategoryBySlug).mockResolvedValue(mockCategory as Category); vi.mocked(productRepository.getProductsByCategory).mockResolvedValue( - mockedProducts + mockedProducts as Product[] ); - // 3. Call getProductsByCategorySlug with valid slug (e.g., "polos") - const products = await getProductsByCategorySlug(mockCategory.slug); - // 4. Assert that getCategoryBySlug was called with correct slug + // Step 3: Call service function + const products = await getProductsByCategorySlug(mockCategory.slug!); + + // Step 4: Verify expected behavior expect(getCategoryBySlug).toHaveBeenCalledWith(mockCategory.slug); - // 5. Assert that getProductsByCategory was called with category.id expect(productRepository.getProductsByCategory).toHaveBeenCalledWith( mockCategory.id ); - // 6. Assert that returned products match the mocked products array expect(products).toEqual(mockedProducts); }); it("should throw error when category slug does not exist", async () => { - // 1. Mock getCategoryBySlug to throw "Category not found" error - // 2. Call getProductsByCategorySlug with invalid slug - // 3. Assert that the function throws the expected error - // 4. Assert that getProductsByCategory was NOT called - }); + // Step 1: Setup - Create test data for non-existent category + const invalidSlug = "invalid-slug"; + + // Step 2: Mock - Configure error response + vi.mocked(getCategoryBySlug).mockRejectedValue( + new Error(`Category with slug "${invalidSlug}" not found`) + ); + + // Step 3: Call service function + const getProducts = getProductsByCategorySlug( + invalidSlug as Category["slug"] + ); - it("should handle repository errors gracefully", async () => { - // 1. Mock getCategoryBySlug to return valid category - // 2. Mock productRepository.getProductsByCategory to throw database error - // 3. Call getProductsByCategorySlug with valid slug - // 4. Assert that the function throws/propagates the repository error + // Step 4: Verify expected behavior + await expect(getProducts).rejects.toThrow( + `Category with slug "${invalidSlug}" not found` + ); + expect(productRepository.getProductsByCategory).not.toHaveBeenCalled(); }); }); describe("getProductById", () => { - it("should return product for valid existing ID", async () => { - // 1. Mock productRepository.getProductById to return a product object - // 2. Call getProductById with valid ID (e.g., 1) - // 3. Assert that getProductById was called with correct ID - // 4. Assert that returned product matches the mocked product - }); + it("should return product for valid ID", async () => { + // Step 1: Setup - Create test data for existing product + const productId = mockProduct.id!; + + // Step 2: Mock - Configure repository response + vi.mocked(productRepository.getProductById).mockResolvedValue( + mockProduct as Product + ); - it("should throw error when product ID does not exist", async () => { - // 1. Mock productRepository.getProductById to return null - // 2. Call getProductById with non-existent ID - // 3. Assert that function throws "Product not found" error + // Step 3: Call service function + const result = await getProductById(productId); + + // Step 4: Verify expected behavior + expect(productRepository.getProductById).toHaveBeenCalledWith(productId); + expect(result).toEqual(mockProduct); }); - it("should handle repository errors gracefully", async () => { - // 1. Mock productRepository.getProductById to throw database error - // 2. Call getProductById with any ID - // 3. Assert that the function throws/propagates the repository error + it("should throw error when product does not exist", async () => { + // Step 1: Setup - Configure ID for non-existent product + const nonExistentId = 999; + + // Step 2: Mock - Configure null response from repository + vi.mocked(productRepository.getProductById).mockResolvedValue(null); + + // Step 3: Call service function + const getProduct = getProductById(nonExistentId); + + // Step 4: Verify expected behavior + await expect(getProduct).rejects.toThrow("Product not found"); }); }); }); From 502eeb2c16c01877947b37dd348a28ecb7f4b4f0 Mon Sep 17 00:00:00 2001 From: Angel Date: Wed, 11 Jun 2025 23:30:16 -0500 Subject: [PATCH 2/2] refactor: enhance test data setup by utilizing createTestCategory and createTestProduct functions added in file: utils.tests.ts --- src/lib/utils.tests.ts | 31 ++++++++++++++++++++ src/services/product.service.test.ts | 44 ++++++++++++---------------- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/src/lib/utils.tests.ts b/src/lib/utils.tests.ts index 9f1dafd..c3ab9e3 100644 --- a/src/lib/utils.tests.ts +++ b/src/lib/utils.tests.ts @@ -2,6 +2,8 @@ import { vi } from "vitest"; import type { User } from "@/models/user.model"; +import type { Category } from "@/models/category.model"; +import type { Product } from "@/models/product.model"; import type { Session } from "react-router"; type TestRequestConfig = { @@ -42,3 +44,32 @@ export const createMockSession = (userId: number | null): Session => ({ flash: vi.fn(), unset: vi.fn(), }); + +export const createTestProduct = (overrides?: Partial): Product => ({ + id: 1, + title: "Test Product", + imgSrc: "/test-image.jpg", + alt: "Test alt text", + price: 100, + description: "Test description", + categoryId: 1, + isOnSale: false, + features: ["Feature 1", "Feature 2"], + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + ...overrides, +}); + +export const createTestCategory = ( + overrides?: Partial +): Category => ({ + id: 1, + title: "Polos", + slug: "polos", + imgSrc: "/images/polos.jpg", + alt: "Colección de polos para programadores", + description: "Explora nuestra colección de polos para programadores", + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + ...overrides, +}); diff --git a/src/services/product.service.test.ts b/src/services/product.service.test.ts index 7ee2fda..4aa3589 100644 --- a/src/services/product.service.test.ts +++ b/src/services/product.service.test.ts @@ -1,5 +1,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +import { createTestCategory, createTestProduct } from "@/lib/utils.tests"; import type { Category } from "@/models/category.model"; import type { Product } from "@/models/product.model"; import * as productRepository from "@/repositories/product.repository"; @@ -11,20 +12,6 @@ import { getProductById, getProductsByCategorySlug } from "./product.service"; vi.mock("@/repositories/product.repository"); vi.mock("./category.service"); -// Test data setup -const mockCategory: Partial = { - id: 1, - slug: "polos", -}; - -const mockProduct: Partial = { - id: 1, - title: "Test Product", - price: 100, - categoryId: mockCategory.id, - description: "Test description", -}; - describe("Product Service", () => { beforeEach(() => { vi.clearAllMocks(); @@ -33,24 +20,29 @@ describe("Product Service", () => { describe("getProductsByCategorySlug", () => { it("should return products for a valid category slug", async () => { // Step 1: Setup - Create test data with valid category and products + const testCategory = createTestCategory(); const mockedProducts = [ - { ...mockProduct, id: 1 }, - { ...mockProduct, id: 2, title: "Test Product 2" }, + createTestProduct({ id: 1, categoryId: testCategory.id }), + createTestProduct({ + id: 2, + title: "Test Product 2", + categoryId: testCategory.id, + }), ]; // Step 2: Mock - Configure repository responses - vi.mocked(getCategoryBySlug).mockResolvedValue(mockCategory as Category); + vi.mocked(getCategoryBySlug).mockResolvedValue(testCategory); vi.mocked(productRepository.getProductsByCategory).mockResolvedValue( mockedProducts as Product[] ); // Step 3: Call service function - const products = await getProductsByCategorySlug(mockCategory.slug!); + const products = await getProductsByCategorySlug(testCategory.slug); // Step 4: Verify expected behavior - expect(getCategoryBySlug).toHaveBeenCalledWith(mockCategory.slug); + expect(getCategoryBySlug).toHaveBeenCalledWith(testCategory.slug); expect(productRepository.getProductsByCategory).toHaveBeenCalledWith( - mockCategory.id + testCategory.id ); expect(products).toEqual(mockedProducts); }); @@ -80,19 +72,21 @@ describe("Product Service", () => { describe("getProductById", () => { it("should return product for valid ID", async () => { // Step 1: Setup - Create test data for existing product - const productId = mockProduct.id!; + const testProduct = createTestProduct(); // Step 2: Mock - Configure repository response vi.mocked(productRepository.getProductById).mockResolvedValue( - mockProduct as Product + testProduct ); // Step 3: Call service function - const result = await getProductById(productId); + const result = await getProductById(testProduct.id); // Step 4: Verify expected behavior - expect(productRepository.getProductById).toHaveBeenCalledWith(productId); - expect(result).toEqual(mockProduct); + expect(productRepository.getProductById).toHaveBeenCalledWith( + testProduct.id + ); + expect(result).toEqual(testProduct); }); it("should throw error when product does not exist", async () => {