ضمیمه و: نمونههای اضافی برای تست (فصل ۹)
این ضمیمه جزئیات کامل پیادهسازی تستهای اضافی برای اپلیکیشن مرکز اجاره کشتیهای کروز را ارائه میدهد، با تمرکز بر تستهای کامپوننتها، مسیرهای سرور، و storeها. این بخش جنبههایی که در فصل نهم یا ضمیمههای اولیه بهصورت خلاصه یا غایب بودند را بهطور جامع پوشش میدهد، تا اطمینان حاصل شود که اپلیکیشن بهطور کامل تست شده و از پایداری و کیفیت بالایی برخوردار است.
نصب و پیکربندی ابزار تست
برای انجام تستها، از Vitest بهعنوان فریمورک تست و @nuxt/test-utils برای ادغام با Nuxt 3 استفاده میکنیم. این ابزارها امکان تست واحد، تست یکپارچگی، و تستهای End-to-End را فراهم میکنند.
نصب وابستگیها
- نصب Vitest و ابزارهای مربوطه:
npm install --save-dev vitest @vue/test-utils @nuxt/test-utils
- بهروزرسانی
package.json:
{
"scripts": {
"test": "vitest",
"test:coverage": "vitest --coverage"
}
}
- ایجاد فایل پیکربندی Vitest (
vitest.config.ts):
import { defineConfig } from 'vitest/config';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
test: {
environment: 'jsdom', // برای شبیهسازی DOM در تستهای Vue
globals: true, // فعالسازی توابع جهانی مانند describe, it, expect
setupFiles: ['./tests/setup.ts'], // فایل تنظیمات اولیه
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
include: ['components/**/*.{vue,ts}', 'stores/**/*.{ts}', 'server/api/**/*.{ts}'],
},
},
});
- فایل تنظیمات اولیه (
tests/setup.ts):
import { config } from '@vue/test-utils';
import { createPinia } from 'pinia';
import { defineNuxtConfig } from 'nuxt/config';
config.global.plugins = [createPinia()];
-
توضیحات:
-
environment: 'jsdom': برای شبیهسازی محیط مرورگر در تستهای Vue. globals: true: نیاز به وارد کردن دستیdescribe,it, وexpectرا حذف میکند.coverage: گزارش پوشش کد برای فایلهای Vue و TypeScript در پوشههایcomponents,stores, وserver/api.
تستهای اضافی
در ادامه، تستهای اضافی برای کامپوننتها، مسیرهای سرور، و storeها ارائه شده است. این تستها مکمل تستهای اولیه ارائهشده برای ShipCard.vue و /api/ships هستند.
۱. تست برای کامپوننت ShipCard.vue
تستهای زیر عملکرد و رندر صحیح کامپوننت ShipCard.vue را بررسی میکنند.
// tests/components/ShipCard.test.ts
import { mount } from '@vue/test-utils';
import ShipCard from '~/components/ShipCard.vue';
import { describe, it, expect } from 'vitest';
describe('ShipCard', () => {
const ship = {
id: 1,
name: 'Ocean Explorer',
capacity: 500,
pricePerDay: 10000,
amenities: ['Pool', 'Gym'],
image: '/images/ocean.jpg',
};
it('renders ship name and price', () => {
const wrapper = mount(ShipCard, { props: { ship } });
expect(wrapper.find('h2').text()).toBe('Ocean Explorer');
expect(wrapper.text()).toContain('$10000/day');
});
it('renders ship image with correct src', () => {
const wrapper = mount(ShipCard, { props: { ship } });
const img = wrapper.find('img');
expect(img.attributes('src')).toContain('/images/ocean.jpg');
});
it('renders link to ship details with correct href', () => {
const wrapper = mount(ShipCard, { props: { ship } });
const link = wrapper.find('a');
expect(link.attributes('href')).toBe('/ships/1');
});
it('emits click event when link is clicked', async () => {
const wrapper = mount(ShipCard, { props: { ship } });
await wrapper.find('a').trigger('click');
expect(wrapper.emitted('click')).toBeTruthy();
});
});
-
توضیحات:
-
تست اول: بررسی میکند که نام و قیمت کشتی بهدرستی رندر شوند.
- تست دوم: اطمینان میدهد که تصویر کشتی با مسیر صحیح نمایش داده میشود.
- تست سوم: بررسی میکند که لینک "View Details" به مسیر درست (
/ships/1) اشاره کند. - تست چهارم: بررسی میکند که کلیک روی لینک رویداد
clickرا منتشر کند (در صورت استفاده از emit).
۲. تست برای کامپوننت BookingForm.vue
تستهای زیر عملکرد فرم رزرو و اعتبارسنجی آن را بررسی میکنند.
// tests/components/BookingForm.test.ts
import { mount } from '@vue/test-utils';
import BookingForm from '~/components/BookingForm.vue';
import { describe, it, expect } from 'vitest';
describe('BookingForm', () => {
const ship = {
id: 1,
name: 'Ocean Explorer',
capacity: 500,
pricePerDay: 10000,
amenities: [],
image: '/images/ocean.jpg',
};
it('renders form inputs', () => {
const wrapper = mount(BookingForm, { props: { ship } });
expect(wrapper.findAll('input[type="date"]').length).toBe(2);
expect(wrapper.find('button[type="submit"]').text()).toBe('Book Now');
});
it('displays validation errors for empty fields', async () => {
const wrapper = mount(BookingForm, { props: { ship } });
await wrapper.find('form').trigger('submit');
expect(wrapper.findAll('.error').length).toBe(2);
expect(wrapper.find('.error').text()).toBe('Start date is required');
});
it('emits submit event with valid data', async () => {
const wrapper = mount(BookingForm, { props: { ship } });
await wrapper.find('input[type="date"]').setValue('2025-10-20');
await wrapper.findAll('input[type="date"]')[1].setValue('2025-10-22');
await wrapper.find('form').trigger('submit');
expect(wrapper.emitted('submit')).toBeTruthy();
expect(wrapper.emitted('submit')[0][0]).toEqual({
startDate: '2025-10-20',
endDate: '2025-10-22',
});
});
});
-
توضیحات:
-
تست اول: بررسی میکند که فیلدهای ورودی و دکمه ارسال فرم بهدرستی رندر شوند.
- تست دوم: اطمینان میدهد که خطاهای اعتبارسنجی برای فیلدهای خالی نمایش داده شوند.
- تست سوم: بررسی میکند که فرم با دادههای معتبر رویداد
submitرا با مقادیر صحیح منتشر کند.
۳. تست برای مسیر /api/ships
تستهای زیر عملکرد مسیر سرور /api/ships را بررسی میکنند.
// tests/server/api/ships.test.ts
import { describe, it, expect } from 'vitest';
import { setup, $fetch } from '@nuxt/test-utils';
describe('Ships API', () => {
it('returns list of ships', async () => {
await setup();
const ships = await $fetch('/api/ships');
expect(ships).toBeInstanceOf(Array);
expect(ships[0]).toHaveProperty('name', 'Ocean Explorer');
expect(ships[0]).toHaveProperty('pricePerDay', 10000);
});
it('returns correct ship data structure', async () => {
await setup();
const ships = await $fetch('/api/ships');
expect(ships[0]).toMatchObject({
id: expect.any(Number),
name: expect.any(String),
capacity: expect.any(Number),
pricePerDay: expect.any(Number),
amenities: expect.any(Array),
image: expect.any(String),
});
});
});
-
توضیحات:
-
تست اول: بررسی میکند که API یک آرایه از کشتیها با دادههای صحیح (مانند
name) برمیگرداند. - تست دوم: اطمینان میدهد که ساختار دادههای بازگشتی با مدل
Shipمطابقت دارد.
۴. تست برای مسیر /api/bookings
تستهای زیر عملکرد مسیر سرور /api/bookings را بررسی میکنند.
// tests/server/api/bookings.test.ts
import { describe, it, expect } from 'vitest';
import { setup, $fetch } from '@nuxt/test-utils';
describe('Bookings API', () => {
it('returns empty array initially', async () => {
await setup();
const bookings = await $fetch('/api/bookings');
expect(bookings).toEqual([]);
});
it('creates a new booking', async () => {
await setup();
const newBooking = {
shipId: 1,
startDate: '2025-10-20',
endDate: '2025-10-22',
totalPrice: 20000,
};
const createdBooking = await $fetch('/api/bookings', {
method: 'POST',
body: newBooking,
});
expect(createdBooking).toMatchObject({
id: expect.any(Number),
shipId: 1,
totalPrice: 20000,
});
const bookings = await $fetch('/api/bookings');
expect(bookings).toContainEqual(createdBooking);
});
});
-
توضیحات:
-
تست اول: بررسی میکند که API در ابتدا آرایه خالی برمیگرداند.
- تست دوم: اطمینان میدهد که رزرو جدید با موفقیت ایجاد شده و در لیست رزروها ظاهر میشود.
۵. تست برای Store (stores/ships.ts)
تستهای زیر عملکرد store مربوط به کشتیها را بررسی میکنند.
// tests/stores/ships.test.ts
import { setActivePinia, createPinia } from 'pinia';
import { useShipsStore } from '~/stores/ships';
import { describe, it, expect, beforeEach } from 'vitest';
import { $fetch } from '@nuxt/test-utils';
vi.mock('@nuxt/test-utils', () => ({
$fetch: vi.fn(),
}));
describe('Ships Store', () => {
beforeEach(() => {
setActivePinia(createPinia());
});
it('fetches ships from API', async () => {
const mockShips = [
{ id: 1, name: 'Ocean Explorer', capacity: 500, pricePerDay: 10000, amenities: [], image: '/images/ocean.jpg' },
];
$fetch.mockResolvedValue(mockShips);
const store = useShipsStore();
await store.fetchShips();
expect(store.ships).toEqual(mockShips);
});
it('filters ships by search query', () => {
const store = useShipsStore();
store.ships = [
{ id: 1, name: 'Ocean Explorer', capacity: 500, pricePerDay: 10000, amenities: [], image: '/images/ocean.jpg' },
{ id: 2, name: 'Luxury Liner', capacity: 1000, pricePerDay: 20000, amenities: [], image: '/images/luxury.jpg' },
];
store.setSearch('Ocean');
expect(store.filteredShips).toHaveLength(1);
expect(store.filteredShips[0].name).toBe('Ocean Explorer');
});
});
-
توضیحات:
-
تست اول: بررسی میکند که متد
fetchShipsدادهها را از API دریافت کرده و در store ذخیره میکند. - تست دوم: اطمینان میدهد که
filteredShipsکشتیها را بر اساس پرسوجوی جستجو فیلتر میکند.
چرا این بخش در ضمیمهها خلاصه بود؟
ضمیمههای اولیه فقط یک تست نمونه برای store ارائه کردند و تستهای جامع برای کامپوننتها (ShipCard.vue, BookingForm.vue) و مسیرهای سرور (/api/ships, /api/bookings) را پوشش ندادند. این ضمیمه با ارائه تستهای اضافی، پوشش کاملتری برای اطمینان از کیفیت کد فراهم میکند.
نکات عملی برای پیادهسازی
-
راهاندازی محیط تست:
-
فایل
vitest.config.tsوtests/setup.tsرا در پروژه ایجاد کنید. -
دستور
npm run testرا برای اجرای تستها وnpm run test:coverageرا برای گزارش پوشش کد اجرا کنید. -
تست در محیط توسعه:
-
با
npm run dev، اپلیکیشن را اجرا کنید و اطمینان حاصل کنید که APIها و کامپوننتها بهدرستی کار میکنند. -
از DevTools مرورگر برای دیباگ استفاده کنید.
-
افزودن تستهای بیشتر:
-
برای
AppHeader.vue:
it('displays user name when authenticated', async () => {
const wrapper = mount(AppHeader, {
global: { plugins: [createPinia()] },
mocks: { $auth: { data: { user: { name: 'John Doe' } } } },
});
expect(wrapper.text()).toContain('John Doe');
});
- برای
/api/stripe-checkout:
it('creates a Stripe checkout session', async () => {
await setup();
const response = await $fetch('/api/stripe-checkout', {
method: 'POST',
body: { booking: { shipId: 1, totalPrice: 20000 } },
});
expect(response).toHaveProperty('id');
});
-
پوشش کد:
-
هدف پوشش حداقل ۸۰٪ برای فایلهای
components,stores, وserver/apiباشد. - گزارشهای پوشش را در پوشه
coverageبررسی کنید.
جمعبندی ضمیمه و
این ضمیمه تستهای اضافی برای کامپوننتها (ShipCard.vue, BookingForm.vue), مسیرهای سرور (/api/ships, /api/bookings), و store (ships.ts) را ارائه داد، همراه با پیکربندی Vitest و نکات عملی. این تستها کیفیت و پایداری اپلیکیشن را تضمین میکنند. برای اطلاعات بیشتر، به فصل نهم یا مخزن فرضی GitHub (https://github.com/khosronz/cruise-rental-app) مراجعه کنید.