獨孤九劍的「破箭式」用於破解各種暗器,在面對暗器時,施展破箭式,可以根據暗器的來勢和攻擊方向,以最快的速度、最有效的角度進行反擊,將暗器擊落或化解其威力。但今天要介紹的設計模式跟破箭式就沒有什麼關係,有時,我們必須要建立測試資料或業務資料,但根據不同的測試目的,可能是從 API 建立、或是直接插入至資料庫等等,有沒有一種設計模式,呼叫的人不需要知道資料如何被建立,但仍舊可以建立資料出來。
Repository Pattern(儲存庫模式)是一種抽象化資料存取層(Data Access Layer, DAL)的設計模式,它將資料存取邏輯與業務邏輯分離,讓應用程式不直接操作資料庫或 API,而是透過一個統一的介面(Repository)來存取與操作資料。在自動化測試中,Repository Pattern 可以用來集中管理測試資料的存取與準備,無論資料來源是資料庫、REST API、GraphQL API 或 JSON 檔,都可以透過相同的方式取得。
假設你有一組測試需要在 UI 測試前建立使用者資料,而測試資料可能來自於
// repositories/UserRepository.ts
export interface UserRepository {
createUser(user: { username: string; password: string }): Promise<void>;
getUser(username: string): Promise<{ username: string; password: string } | null>;
}
// repositories/PostgresUserRepository.ts
import { Client } from 'pg';
import { UserRepository } from './UserRepository';
export class PostgresUserRepository implements UserRepository {
async createUser(user) {
const client = new Client({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
});
await client.connect();
await client.query('INSERT INTO users (username, password) VALUES ($1, $2)',
[user.username, user.password]);
await client.end();
}
async getUser(username) {
const client = new Client({ /* 同上設定 */ });
await client.connect();
const res = await client.query('SELECT username, password FROM users WHERE username = $1', [username]);
await client.end();
return res.rows[0] || null;
}
}
// repositories/ApiUserRepository.ts
import axios from 'axios';
import { UserRepository } from './UserRepository';
export class ApiUserRepository implements UserRepository {
async createUser(user) {
await axios.post(`${process.env.API_BASE_URL}/users`, user);
}
async getUser(username) {
const res = await axios.get(`${process.env.API_BASE_URL}/users/${username}`);
return res.data;
}
}
// tests/user-login.spec.ts
import { test, expect } from '@playwright/test';
import { ApiUserRepository } from '../repositories/ApiUserRepository';
test('使用正確帳密登入成功', async ({ page }) => {
const repo = new ApiUserRepository();
const testUser = { username: 'user01', password: 'pass123' };
await repo.createUser(testUser);
await page.goto('/login');
await page.getByTestId('login-username').fill(testUser.username);
await page.getByTestId('login-password').fill(testUser.password);
await page.getByTestId('login-submit').click();
await expect(page).toHaveURL(/home/);
});
Repository Pattern 在 Playwright 測試中,可以讓 資料準備與清理更彈性,同時讓 UI 測試專注在操作與驗證,降低對資料存取細節的依賴,提升可維護性與可重用性。