API 測試
簡介
Playwright 可以用來存取你的應用程式的 REST API。
有時你可能想要直接從 Node.js 向伺服器發送請求,而不需要加載頁面並在其中執行 js 程式碼。以下是一些可能會派上用場的範例:
- 測試你的伺服器 API。
- 在測試中訪問 web 應用程式之前準備伺服器端狀態。
- 在瀏覽器中執行一些操作後驗證伺服器端後置條件。
所有這些都可以通過 APIRequestContext 方法實現。
撰寫 API 測試
APIRequestContext 可以透過網路發送各種 HTTP(S) 請求。
以下範例展示如何使用 Playwright 測試通過 GitHub API 建立問題。測試套件將執行以下操作:
- 建立一個新的儲存庫後執行測試。
- 建立一些問題並驗證伺服器狀態。
- 執行測試後刪除儲存庫。
設定
GitHub API 需要授權,因此我們將為所有測試設定一次性 token。在此同時,我們也會設定 baseURL
以簡化測試。你可以將它們放在配置文件中,或者在測試文件中使用 test.use()
。
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
// All requests we send go to this API endpoint.
baseURL: 'https://api.github.com',
extraHTTPHeaders: {
// We set this header per GitHub guidelines.
'Accept': 'application/vnd.github.v3+json',
// Add authorization token to all requests.
// Assuming personal access token available in the environment.
'Authorization': `token ${process.env.API_TOKEN}`,
},
}
});
代理配置
如果你的測試需要在代理伺服器後面執行,你可以在配置中指定這一點,request
固定裝置會自動選取它:
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
proxy: {
server: 'http://my-proxy:8080',
username: 'user',
password: 'secret'
},
}
});
撰寫測試
Playwright Test 內建 request
fixture,遵循我們指定的 baseURL
或 extraHTTPHeaders
等配置選項,並準備發送一些請求。
現在我們可以 新增一些測試,這些測試將在儲存庫中建立新的問題。
const REPO = 'test-repo-1';
const USER = 'github-username';
test('should create a bug report', async ({ request }) => {
const newIssue = await request.post(`/repos/${USER}/${REPO}/issues`, {
data: {
title: '[Bug] report 1',
body: 'Bug description',
}
});
expect(newIssue.ok()).toBeTruthy();
const issues = await request.get(`/repos/${USER}/${REPO}/issues`);
expect(issues.ok()).toBeTruthy();
expect(await issues.json()).toContainEqual(expect.objectContaining({
title: '[Bug] report 1',
body: 'Bug description'
}));
});
test('should create a feature request', async ({ request }) => {
const newIssue = await request.post(`/repos/${USER}/${REPO}/issues`, {
data: {
title: '[Feature] request 1',
body: 'Feature description',
}
});
expect(newIssue.ok()).toBeTruthy();
const issues = await request.get(`/repos/${USER}/${REPO}/issues`);
expect(issues.ok()).toBeTruthy();
expect(await issues.json()).toContainEqual(expect.objectContaining({
title: '[Feature] request 1',
body: 'Feature description'
}));
});
設定和拆卸
這些測試假設資料庫已存在。你可能想在執行測試之前建立一個新的,並在之後刪除它。使用 beforeAll
和 afterAll
鉤子來實現。
test.beforeAll(async ({ request }) => {
// Create a new repository
const response = await request.post('/user/repos', {
data: {
name: REPO
}
});
expect(response.ok()).toBeTruthy();
});
test.afterAll(async ({ request }) => {
// Delete the repository
const response = await request.delete(`/repos/${USER}/${REPO}`);
expect(response.ok()).toBeTruthy();
});
使用請求上下文
幕後,request
fixture 實際上會呼叫 apiRequest.newContext()。如果你想要更多控制權,你可以手動執行。以下是一個獨立的腳本,它與上面的 beforeAll
和 afterAll
做同樣的事情。
import { request } from '@playwright/test';
const REPO = 'test-repo-1';
const USER = 'github-username';
(async () => {
// Create a context that will issue http requests.
const context = await request.newContext({
baseURL: 'https://api.github.com',
});
// Create a repository.
await context.post('/user/repos', {
headers: {
'Accept': 'application/vnd.github.v3+json',
// Add GitHub personal access token.
'Authorization': `token ${process.env.API_TOKEN}`,
},
data: {
name: REPO
}
});
// Delete a repository.
await context.delete(`/repos/${USER}/${REPO}`, {
headers: {
'Accept': 'application/vnd.github.v3+json',
// Add GitHub personal access token.
'Authorization': `token ${process.env.API_TOKEN}`,
}
});
})();
從 UI 測試發送 API 請求
在瀏覽器內執行測試時,您可能希望呼叫應用程式的 HTTP API。如果您需要在執行測試之前準備伺服器狀態或在瀏覽器中執行某些操作後檢查伺服器上的一些後置條件,這可能會很有幫助。所有這些都可以通過 APIRequestContext 方法來實現。
建立前提條件
以下測試通過 API 建立一個新問題,然後導航到專案中所有問題的列表,以檢查它是否出現在列表頂部。
import { test, expect } from '@playwright/test';
const REPO = 'test-repo-1';
const USER = 'github-username';
// Request context is reused by all tests in the file.
let apiContext;
test.beforeAll(async ({ playwright }) => {
apiContext = await playwright.request.newContext({
// All requests we send go to this API endpoint.
baseURL: 'https://api.github.com',
extraHTTPHeaders: {
// We set this header per GitHub guidelines.
'Accept': 'application/vnd.github.v3+json',
// Add authorization token to all requests.
// Assuming personal access token available in the environment.
'Authorization': `token ${process.env.API_TOKEN}`,
},
});
});
test.afterAll(async ({ }) => {
// Dispose all responses.
await apiContext.dispose();
});
test('last created issue should be first in the list', async ({ page }) => {
const newIssue = await apiContext.post(`/repos/${USER}/${REPO}/issues`, {
data: {
title: '[Feature] request 1',
}
});
expect(newIssue.ok()).toBeTruthy();
await page.goto(`https://github.com/${USER}/${REPO}/issues`);
const firstIssue = page.locator(`a[data-hovercard-type='issue']`).first();
await expect(firstIssue).toHaveText('[Feature] request 1');
});