Intercept HTTP Requests
When you interact with a web page, the page issues HTTP requests. Additionally, you can execute arbitrary HTTP requests with the request
method.
TestCafe allows you intercept these requests with request hooks.
- Log HTTP Requests to track your application’s network activity.
- Mock HTTP Requests to feed dummy data to your application.
- Create Custom Request Hooks to perform other actions with HTTP requests, for example, emulate Kerberos or Client Certificate Authentication.
You can attach request hooks to specific tests and fixtures or define them globally.
Log HTTP Requests
Use the request logger to record outgoing HTTP requests and inspect responses to these requests. For instance, you may want to make sure that the data from a remote service is correct.
Use the RequestLogger constructor to create a request logger.
import { RequestLogger } from 'testcafe';
const simpleLogger = RequestLogger('http://example.com');
const headerLogger = RequestLogger(/testcafe/, {
logRequestHeaders: true,
logResponseHeaders: true
});
Attach the logger to a specific test / fixture or enable it globally.
The RequestLogger
logs the following data about outgoing requests:
- The request’s target URL
- The request’s HTTP method
- The status code of the response
- The request sender’s user agent string
Use the following RequestLogger
methods to inspect this data:
Member | Description |
---|---|
requests | Returns an array of logged requests |
contains | Succeeds if the logger contains a matching request |
count | Returns the number of matching requests |
clear | Clears all logged requests |
Example
import { Selector, RequestLogger } from 'testcafe';
import fs from 'fs';
import path from 'path';
const url = 'https://demos.devexpress.com/ASPxGridViewDemos/Exporting/Exporting.aspx';
const logger = RequestLogger({ url, method: 'post' }, {
logResponseHeaders: true,
logResponseBody: true
});
fixture `Export`
.page(url)
.requestHooks(logger);
test('export to csv', async t => {
const exportToCSVButton = Selector('span').withText('Export to CSV');
await t
.click(exportToCSVButton)
// When you click 'Export', your browser downloads a compressed CSV file (*.gzip).
.expect(logger.contains(r => r.response.statusCode === 200)).ok();
const filePath = path.join(__dirname, 'exported-grid.zip');
// Locates the file on the disk
console.log(filePath);
console.log(logger.requests[0].response.headers);
fs.writeFileSync(filePath, logger.requests[0].response.body);
// Use 3rd party modules to unpack the archive,
// parse the CSV and check the data.
// Alternatively, verify the file manually.
});
Mock HTTP Requests
Use the request mocker to intercept your application’s HTTP requests and respond to them with dummy data. This is useful when some parts of your website’s infrastructure are difficult to deploy or costly to use.
Imagine that your application sends data to a third-party traffic analysis service. You do not want your functional tests to impact your website’s analytics. Add a request mocker to intercept requests to that service, and emulate the response as needed.
Use the RequestMock constructor to create a request mocker.
var mock = RequestMock();
Append the onRequestTo and respond methods to the RequestMock
constructor:
var mock = RequestMock()
.onRequestTo(request1)
.respond(responseMock1)
The onRequestTo method specifies which requests to intercept. The respond specifies how TestCafe should respond to the intercepted request.
If you need to mock multiple requests, you can chain these methods repeatedly:
var mock = RequestMock()
.onRequestTo(request1)
.respond(responseMock1)
.onRequestTo(request2)
.respond(responseMock2)
To enable this request hook, attach it to a test/fixture or define it globally.
Example
import { Selector, RequestMock } from 'testcafe';
// Google Analytics API endpoint
const collectDataGoogleAnalyticsRegExp = new RegExp('https://www.google-analytics.com/collect');
// Binary image data for the mocked response
const mockedResponse = Buffer.from([0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01]);
const mock = RequestMock()
.onRequestTo(collectDataGoogleAnalyticsRegExp)
// This hook fires when the application sends a request to the Google Analytics API endpoint
.respond(mockedResponse, 202, {
'content-length': mockedResponse.length,
'content-type': 'image/gif'
});
// TestCafe intercepts the request and responds with the dummy data above (status code: 202).
fixture `Fixture`
.page('https://devexpress.github.io/testcafe/')
.requestHooks(mock);
test('basic', async t => {
await t
.click('.get-started-button')
.debug();
// The debug action pauses the test.
// Open your browser's Developer Tools to inspect the request and the response.
});
Create a Custom Request Hook
Create custom request hooks to perform other actions with HTTP requests.
- Create a new class that inherits from the RequestHook class.
- Override the onRequest method to handle the request before it is sent.
- Override the onResponse method to handle the response after it is received.
import { RequestHook } from 'testcafe';
export class MyRequestHook extends RequestHook {
constructor (requestFilterRules, responseEventConfigureOpts) {
super(requestFilterRules, responseEventConfigureOpts);
// ...
}
async onRequest (event) {
// ...
}
async onResponse (event) {
// ...
}
}
- Create an instance of your new hook. Attach it to a test/fixture or define it globally.
import { MyRequestHook } from './my-request-hook';
const customHook = new MyRequestHook(/https?:\/\/example.com/);
fixture `My fixture`
.page('http://example.com')
.requestHooks(customHook);
test('My test', async t => {
// test actions
});
Example
import { Selector, RequestHook } from 'testcafe';
class JwtBearerAuthorization extends RequestHook {
constructor () {
// No URL filtering applied to this hook
// so it will be used for all requests.
super();
}
onRequest (e) {
e.requestOptions.headers['Authorization'] = 'generate token here';
}
onResponse (e) {
// This method must also be overridden,
// but you can leave it blank.
}
}
const jwtBearerAuthorization = new JwtBearerAuthorization();
fixture `Fixture`
.page('<website URL>')
.requestHooks(jwtBearerAuthorization);
test('basic', async t => {
/* some actions */
});
Attach Hooks to Tests and Fixtures
Attach a hook to a test or fixture to handle HTTP requests within these entities.
- Use the fixture.requestHooks method to attach a hook to a fixture.
- Use the test.requestHooks method to attach a hook to a test.
- Use t.addRequestHooks and t.removeRequestHooks to attach and detach hooks on the fly.
import { RequestLogger, RequestMock } from 'testcafe';
const logger = RequestLogger('http://example.com');
const mock = RequestMock()
.onRequestTo('http://external-service.com/api/')
.respond({ data: 'value' });
fixture `My fixture`
.page('http://example.com')
.requestHooks(logger);
test
.requestHooks(mock)
('My test', async t => {
await t
.click('#send-logged-request')
.expect(logger.count(() => true)).eql(1)
.removeRequestHooks(logger)
.click('#send-unlogged-request')
.expect(logger.count(() => true)).eql(1)
.addRequestHooks(logger)
.click('#send-logged-request')
.expect(logger.count(() => true)).eql(2);
});
Define Global Hooks in the Configuration File
You may need to attach the same hook to multiple tests or fixtures. To avoid code duplication, you can define a global hook in the JavaScript configuration file.
The following code shows an example that defines a global RequestMock
. Note that test code doesn’t require an explicit hook declaration. The configuration file attaches the hook to the test.
//.testcaferc.js
const { RequestMock } = require('testcafe');
const mock = RequestMock()
.onRequestTo('https://api.mycorp.com/users/id/135865')
.respond({
name: 'John Hearts',
position: 'CTO',
}, 200, { 'access-control-allow-origin': '*' })
.onRequestTo(/internal.mycorp.com/)
.respond(null, 404);
module.exports = {
hooks: {
request: mock,
},
};
//test.js
fixture `RequestMock`
.page`https://mycorp.com`;
test('Should mock requests', async t => {
const user = await t
.eval(() => fetch('https://api.mycorp.com/users/id/135865')
.then(res => res.json()));
await t
.expect(user).eql({ name: 'John Hearts', position: 'CTO' })
.navigateTo('https://internal.mycorp.com');
});
The hook declaration may require an extra step. In the example above, the test does not need to read any data from the RequestMock
object. The situation changes if the hook is a RequestLogger
or a custom RequestMock
. Tests may need to obtain the hook object’s data in such cases.
The following example demonstrates how you can declare a global RequestLogger
. The test needs to access the logger object in order to read and validate its data. To enable such access, define an extra file: “logger.js”. This file defines the hook object and supplies it to both the test and the configuration file.
//logger.js
const { RequestLogger } = require('testcafe');
module.exports = new RequestLogger();
//.testcaferc.js
const logger = require('./logger');
module.exports = {
hooks: {
request: logger,
},
};
//test.js
const { logger } = './logger';
fixture`RequestLogger`
.page('https://devexpress.github.io/testcafe/example/');
test('Check request', async t => {
await t
.expect(logger.contains(record => record.response.statusCode === 200)).ok()
.expect(logger.requests[0].request.method).eql('get');
});
Request Hook Conflicts
If a single request is subject to multiple request hooks, TestCafe executes the hooks in the following order:
- Global hook
- Fixture hook
- Test hook