| name | non-functional-testing |
| description | Use when designing or reviewing non-functional test approaches for performance, security, accessibility, and reliability concerns within Cypress specs. |
Principles
INVERSION: ask "what guarantees this system is slow, insecure, unreliable?" then check what we are NOT testing — otherwise forward coverage silently misses the attribute nobody asserted
EMBEDDING: non-functional checks ride inside functional automation as dynamic assertions, not a separate suite — otherwise the quality attribute drifts once it lives outside the flow that exercises it
ISOLATION: a non-functional assertion lives in its own it block — otherwise a slow response fails the same verdict as a wrong response and breaks the functional flow
FLAKINESS_CONTAINMENT: timing and environment-sensitive checks stay off by default and run in a dedicated CI stage — otherwise always-on latency assertions turn green suites red on noise
LOCAL_QUALITY: threshold and rigor track the operation — otherwise a read is held to a write's latency and a happy path is probed as hard as a boundary, mispricing both
Method
Activation
ENV_GATE: NFR_CHECKS=true from CI enables the checks, disabled by default
GUARD: if (Cypress.env('NFR_CHECKS')) wraps the non-functional context inside a command; reusable assertion commands guard internally so callers never manage the flag
CI_USAGE: NFR_CHECKS=true LANGUAGE=en COLOUR_THEME=default TARGET_ENV=qa npm run test
THRESHOLD_SOURCE: import limits from constraints, never hardcode
Inline command integration
STRUCTURE: command holds context → it blocks for non-functional assertions, skipped when the env is off
FAILURE_MODE: Cypress assertion failure carrying a descriptive message
Cypress.Commands.add('moduleName__retrieve__GET', (token, id, restOptions = {}) => {
return cy.request({ method: 'GET', url: apiUrls.module.byId(id), auth: { bearer: token }, ...restOptions }).then((response) => {
if (Cypress.env('NFR_CHECKS')) {
context('Module.Submodule.Retrieve.GET: When response is received — non-functional', () => {
it('Module.Submodule.Retrieve.GET: Then response time is within threshold', () => {
expect(response.duration).to.be.lessThan(PERF.MAX_RESPONSE_MS);
});
it('Module.Submodule.Retrieve.GET: Then no sensitive fields are exposed', () => {
expect(JSON.stringify(response.body)).to.not.match(/password|secret/i);
});
});
}
return response;
});
});
Reusable assertion commands
NAMING: nfr__ prefix marks a shared non-functional assertion command callable from any spec or command
LOCATION: cypress/commands/api/ for API checks, cypress/commands/ui/ for UI checks
API_SCOPE: response duration, payload size, sensitive fields, status code ranges, header presence
UI_SCOPE: element alignment, colour contrast, focus visibility, aria attributes, render timing
COMPOSABILITY: chain assertion commands for layered checks — cy.nfr__assertResponsePerformance(response) inside any API command, cy.get(selector).nfr__assertElementAccessibility() on any UI element
Cypress.Commands.add('nfr__assertResponsePerformance', (response, threshold = PERF.MAX_RESPONSE_MS) => {
if (!Cypress.env('NFR_CHECKS')) return;
expect(response.duration, `response time < ${threshold}ms`).to.be.lessThan(threshold);
});
Cypress.Commands.add('nfr__assertNoSensitiveFields', (response) => {
if (!Cypress.env('NFR_CHECKS')) return;
SECURITY.SENSITIVE_FIELDS.forEach((field) => {
expect(response.body).to.not.have.property(field);
});
});
Cypress.Commands.add('nfr__assertElementAccessibility', { prevSubject: 'element' }, (subject) => {
if (!Cypress.env('NFR_CHECKS')) return cy.wrap(subject);
cy.wrap(subject).should('have.attr', 'aria-label').or('have.attr', 'aria-labelledby');
return cy.wrap(subject);
});
Cypress.Commands.add('nfr__assertColourContrast', { prevSubject: 'element' }, (subject, expectedColour) => {
if (!Cypress.env('NFR_CHECKS')) return cy.wrap(subject);
cy.wrap(subject).should('have.css', 'color', expectedColour);
return cy.wrap(subject);
});
Performance
RESPONSE_TIME: assert response.duration < threshold per endpoint, measured separately so a slow one is pinned
PAYLOAD_SIZE: assert JSON.stringify(response.body).length < MAX_PAYLOAD_BYTES
PAGINATION: check list-response efficiency against its own list threshold
Security
AUTH_ENFORCEMENT: prove unauthorized fails and role-based access is denied — assert the negative, not only the happy path
LAYERED_AUTH: probe auth at transport → resource → field level
PRELIMINARY_CHECK: verify auth is required before testing the feature behind it
SENSITIVE_FIELDS: assert response body excludes password, token, secret fields
INPUT_SANITIZATION: prove invalid and boundary input is rejected — probe aggressively at auth, input, and file-upload boundaries
LEAKAGE: verify error responses carry no stack traces
Reliability
ERROR_HANDLING: assert malformed input is rejected and state stays consistent after a failure
DATA_INTEGRITY: verify concurrent-modification handling and idempotency
MEDIATOR: cy.intercept() observes network behaviour without modifying it
Accessibility
ARIA: assert aria-label or aria-labelledby presence on interactive elements
CONTRAST: assert color css against the expected colour
FOCUS: assert focus visibility and element alignment
Scope
INCLUDES: response-time assertions, payload-size checks, auth validation, input sanitization, error handling, component alignment, accessibility checks, reliability monitoring
EXCLUDES: dedicated load testing, penetration testing, visual regression
Constraints template
export const PERF = Object.freeze({ MAX_RESPONSE_MS: 2000, MAX_LIST_RESPONSE_MS: 3000, MAX_PAYLOAD_BYTES: 1_000_000 });
export const SECURITY = Object.freeze({ UNAUTHORIZED_STATUS: 401, FORBIDDEN_STATUS: 403, SENSITIVE_FIELDS: ['password', 'token', 'secret'] });
Validation
COVERAGE_CHECK: security and performance contexts exist for critical endpoints
THRESHOLD_CHECK: thresholds sourced from constraints, not hardcoded
AUTH_CHECK: every protected endpoint tested with invalid and missing tokens
ISOLATION_CHECK: non-functional assertions live in own it blocks, functional flow unaffected
SCOPE_CHECK: within functional automation scope, no load or pen-test overlap