Mock e Spy per le Richieste
WebdriverIO include un supporto integrato per modificare le risposte di rete che ti permette di concentrarti sul testing della tua applicazione frontend senza dover configurare il backend o un server mock. Puoi definire risposte personalizzate per risorse web come richieste API REST nel tuo test e modificarle dinamicamente.
Nota che l'utilizzo del comando mock
richiede il supporto per il protocollo Chrome DevTools. Questo supporto è garantito se esegui i test localmente in un browser basato su Chromium, tramite Selenium Grid v4 o versioni successive, o attraverso un provider cloud con supporto per il protocollo Chrome DevTools (ad esempio SauceLabs, BrowserStack, LambdaTest). Il supporto completo per tutti i browser sarà disponibile una volta che le primitive necessarie saranno implementate in Webdriver Bidi e integrate nei rispettivi browser.
Creazione di un mock
Prima di poter modificare qualsiasi risposta devi definire un mock. Questo mock è descritto dall'URL della risorsa e può essere filtrato per metodo di richiesta o header. La risorsa supporta espressioni glob tramite minimatch:
// mock di tutte le risorse che terminano con "/users/list"
const userListMock = await browser.mock('**/users/list')
// oppure puoi specificare il mock filtrando le risorse per header o
// codice di stato, solo mock di richieste riuscite per risorse json
const strictMock = await browser.mock('**', {
// mock di tutte le risposte json
requestHeaders: { 'Content-Type': 'application/json' },
// che hanno avuto successo
statusCode: 200
})
Specificare risposte personalizzate
Una volta definito un mock, puoi definire risposte personalizzate per esso. Queste risposte personalizzate possono essere un oggetto per rispondere con un JSON, un file locale per rispondere con un fixture personalizzato o una risorsa web per sostituire la risposta con una risorsa da internet.
Simulazione di richieste API
Per simulare richieste API in cui ti aspetti una risposta JSON, tutto ciò che devi fare è chiamare respond
sull'oggetto mock con un oggetto arbitrario che desideri restituire, ad esempio:
const mock = await browser.mock('https://todo-backend-express-knex.herokuapp.com/')
mock.respond([{
title: 'Injected (non) completed Todo',
order: null,
completed: false
}, {
title: 'Injected completed Todo',
order: null,
completed: true
}], {
headers: {
'Access-Control-Allow-Origin': '*'
},
fetchResponse: false
})
await browser.url('https://todobackend.com/client/index.html?https://todo-backend-express-knex.herokuapp.com/')
await $('#todo-list li').waitForExist()
console.log(await $$('#todo-list li').map(el => el.getText()))
// output: "[ 'Injected (non) completed Todo', 'Injected completed Todo' ]"
Puoi anche modificare gli header della risposta e il codice di stato passando alcuni parametri di risposta mock come segue:
mock.respond({ ... }, {
// risponde con codice di stato 404
statusCode: 404,
// unisce gli header di risposta con i seguenti header
headers: { 'x-custom-header': 'foobar' }
})
Se non vuoi che il mock chiami il backend, puoi passare false
per il flag fetchResponse
.
mock.respond({ ... }, {
// non chiamare il backend effettivo
fetchResponse: false
})
Si consiglia di memorizzare le risposte personalizzate in file fixture in modo da poterli importare nel test come segue:
// richiede Node.js v16.14.0 o superiore per supportare le asserzioni di importazione JSON
import responseFixture from './__fixtures__/apiResponse.json' assert { type: 'json' }
mock.respond(responseFixture)
Simulazione di risorse testuali
Se desideri modificare risorse testuali come file JavaScript, CSS o altre risorse basate su testo, puoi semplicemente passare un percorso di file e WebdriverIO sostituirà la risorsa originale con essa, ad esempio:
const scriptMock = await browser.mock('**/script.min.js')
scriptMock.respond('./tests/fixtures/script.js')
// o rispondi con il tuo JS personalizzato
scriptMock.respond('alert("I am a mocked resource")')
Reindirizzamento di risorse web
Puoi anche sostituire una risorsa web con un'altra risorsa web se la risposta desiderata è già ospitata sul web. Questo funziona sia con singole risorse di pagina che con una pagina web stessa, ad esempio:
const pageMock = await browser.mock('https://google.com/')
await pageMock.respond('https://webdriver.io')
await browser.url('https://google.com')
console.log(await browser.getTitle()) // restituisce "WebdriverIO · Next-gen browser and mobile automation test framework for Node.js"
Risposte dinamiche
Se la tua risposta mock dipende dalla risposta originale della risorsa, puoi anche modificare dinamicamente la risorsa passando una funzione che riceve la risposta originale come parametro e imposta il mock in base al valore restituito, ad esempio:
const mock = await browser.mock('https://todo-backend-express-knex.herokuapp.com/', {
method: 'get'
})
mock.respond((req) => {
// sostituisce il contenuto todo con il loro numero di lista
return req.body.map((item, i) => ({ ...item, title: i }))
})
await browser.url('https://todobackend.com/client/index.html?https://todo-backend-express-knex.herokuapp.com/')
await $('#todo-list li').waitForExist()
console.log(await $$('#todo-list li label').map((el) => el.getText()))
// restituisce
// [
// '0', '1', '2', '19', '20',
// '21', '3', '4', '5', '6',
// '7', '8', '9', '10', '11',
// '12', '13', '14', '15', '16',
// '17', '18', '22'
// ]
Interruzione dei mock
Invece di restituire una risposta personalizzata, puoi anche semplicemente interrompere la richiesta con uno dei seguenti errori HTTP:
- Failed
- Aborted
- TimedOut
- AccessDenied
- ConnectionClosed
- ConnectionReset
- ConnectionRefused
- ConnectionAborted
- ConnectionFailed
- NameNotResolved
- InternetDisconnected
- AddressUnreachable
- BlockedByClient
- BlockedByResponse
Questo è molto utile se vuoi bloccare script di terze parti dalla tua pagina che hanno un'influenza negativa sul tuo test funzionale. Puoi interrompere un mock semplicemente chiamando abort
o abortOnce
, ad esempio:
const mock = await browser.mock('https://www.google-analytics.com/**')
mock.abort('Failed')
Spy
Ogni mock è automaticamente uno spy che conta il numero di richieste che il browser ha fatto a quella risorsa. Se non applichi una risposta personalizzata o un motivo di interruzione al mock, questo continua con la risposta predefinita che normalmente riceveresti. Ciò ti consente di controllare quante volte il browser ha effettuato la richiesta, ad esempio a un determinato endpoint API.
const mock = await browser.mock('**/user', { method: 'post' })
console.log(mock.calls.length) // restituisce 0
// registra utente
await $('#username').setValue('randomUser')
await $('password').setValue('password123')
await $('password_repeat').setValue('password123')
await $('button[type="submit"]').click()
// verifica se è stata effettuata la richiesta API
expect(mock.calls.length).toBe(1)
// verifica la risposta
expect(mock.calls[0].body).toEqual({ success: true })