Nightwatch 中的 API 測試
概觀
API 測試是一種軟體測試,涉及測試應用程式的 API 層。
API 測試包括測試用戶端應用程式和伺服器之間的請求和回應。這通常透過將 HTTP 請求傳送到 API 端點並驗證傳回的回應來完成。API 測試的主要目標是確保 API 行為符合預期,並針對不同的輸入情境傳回正確的資料和錯誤。
總體而言,API 測試是軟體測試的重要環節,可確保應用程式 API 層的可靠性和功能,讓開發人員能夠建置穩健且可擴充的軟體應用程式。
運作方式為何?
若要執行 API 測試,需要安裝官方的 @nightwatch/apitesting 外掛程式。此外掛程式提供以下功能
需要 Nightwatch 2.6.4 或更高版本。
安裝
1) 從 NPM 安裝外掛程式
npm i @nightwatch/apitesting --save-dev
2) 將外掛程式新增至清單
更新 Nightwatch 組態以將此外掛程式新增至清單
module.exports = {
plugins: ['@nightwatch/apitesting']
// other Nightwatch settings...
}
3) 停用瀏覽器 Session
我們還需要關閉瀏覽器 Session,因為我們只執行 API 測試。這可以透過為 API 測試新增一個新環境來達成,如下所示在 nightwatch.conf.js 中。
module.exports = {
// ....
api_testing: {
start_session: false,
webdriver: {
start_process: false,
}
}
}
組態設定
此外掛程式目前只有一個組態選項,即是否要將 HTTP 回應記錄到主控台。這可以在 nightwatch.json
(或 nightwatch.conf.js
) 組態檔中設定。
{
"@nightwatch/apitesting" : {
"log_responses": true
}
}
測試 API 標頭和回應
由於 Nightwatch 在幕後使用 supertest
,您可以測試不同類型的 REST API 標頭和回應。
GET 請求
describe('api testing', function () {
it('get api test', async function({supertest}) {
await supertest
.request("https://petstore.swagger.io/v2")
.get("/pet/findByStatus?status=available")
.expect(200)
.expect('Content-Type', /json/)
.then(function(response){
expect(response._body.length).to.be.greaterThan(0);
});
});
});
POST 請求
describe('api testing', function () {
it('post api test', async function({supertest}) {
await supertest
.request("https://petstore.swagger.io/v2")
.post("/pet")
.send({
"id": 0,
"category": {
"id": 0,
"name": "string"
},
"name": "doggie",
"photoUrls": [
"string"
],
"tags": [
{
"id": 0,
"name": "string"
}
],
"status": "available"
})
.expect(200)
.expect('Content-Type', /json/)
.then(function(response){
expect(response._body.name).to.be.equal("doggie");
});
});
});
執行 API 測試
確保 API 測試針對 start_session
和 webdriver -> start_process
設定為 false
的 environment
執行。
npx nightwatch <path to tests> --env api_testing
HTML 報告
執行測試後,可以在 HTML 報告中檢閱結果。
整合式模擬伺服器
@nightwatch/apitesting
外掛程式還提供以 express 為基礎的內建模擬伺服器,可用於斷言傳入的 http 請求。
以下是一個範例模擬伺服器
describe('api testing with supertest in nightwatch POST', function () {
let server;
before(async function(client) {
server = await client.mockserver.create();
server.setup((app) => {
app.post('/api/v1/datasets/', function (req, res) {
res.status(200).json({
id: 'test-dataset-id'
});
});
});
await server.start(3000);
});
after(() => {
server.close();
});
it('demo test', async function(client) {
const req = await server.request()
.post('/api/v1/datasets/')
.send({name: 'medea'})
.set('Accept', 'application/json')
.expect(200)
.expect('Content-Type', /json/);
await client.assert.deepStrictEqual(server.route.post('/api/v1/datasets/').requestBody, {name: 'medea'});
});
});
模擬伺服器 API
const mockServer = await client.mockserver.create()
– 建立新的模擬伺服器執行個體await mockServer.setup(definition)
– 使用提供的路由定義設定現有的模擬伺服器執行個體 範例await mockServer.setup((app) => { app.get('/api/v1/schemas', function (req, res) { console.log('GET /api/v1/schemas called');
res.status(200).json([ { id: 'test-schema-id1' }, { id: 'test-schema-id2' } ]); }) });await mockServer.start(port)
– 在指定的連接埠上啟動現有的模擬伺服器執行個體await mockServer.route(path)
– 傳回指定路由上的 sinon spy
斷言傳入的請求
使用 mockServer.route(path)
方法擷取指定路由上的 Spy。然後,您可以使用 sinon 斷言來斷言傳入的請求。
範例
考慮先前的模擬伺服器設定範例。如果我們想要斷言呼叫了 GET /api/v1/schemas
路由,我們可以執行以下操作
it('demo test', async function(client) {
client
.assert.strictEqual(mockServer.route.get('/api/v1/schemas').calledOnce, true, 'called once')
.assert.strictEqual(mockServer.route.get('/api/v1/schemas').calledTwice, false);
});
斷言請求標頭
我們也可以斷言請求標頭,例如使用內建的 expect()
斷言 API,此 API 使用 chai
it('demo test', async function(client) {
const {requestHeaders} = mockServer.route.get('/api/v1/schemas');
client.expect(requestHeaders).to.have.property('connection', 'close');
});
斷言傳入的 Post 資料
我們也可以斷言傳入的 Post 資料
- 首先,為模擬伺服器設定 Post 路由
await mockServer.setup((app) => {
app.post('/api/v1/datasets/', function (req, res) {
res.status(200).json({
id: 'test-dataset-id'
});
});
});
- 然後,使用
mockServer.route.post(path)
方法擷取指定路由上的 Spy。然後,您可以使用 sinon 斷言來斷言傳入的請求。
it('demo test', async function(client) {
const {requestBody} = mockServer.route.post('/api/v1/schemas');
await client.assert.deepStrictEqual(requestBody, {name: 'medea'});
});
若要等待傳入的請求測試,您可以使用 waitUntil()
指令。
使用 waitUntil
的範例
it('demo test', async function(client) {
const timeoutMs = 15000;
const retryIntervalMs = 500;
await client.waitUntil(async function () {
const spy = server.route.get('/api/v1/schemas');
if (spy) {
return spy.calledOnce;
}
return false;
}, timeoutMs, retryIntervalMs, new Error(`time out reached (10000ms) while waiting for API call.`));
});