Primary reference: https://cap.cloud.sap/docs/node.js/cds-test Vitest support (primary): https://cap.cloud.sap/docs/releases/2026/apr26
Recommended test runner: Vitest (CAP April 2026+)
cds.test now fully supports Vitest as a primary choice, while still maintaining compatibility with other test runners like Jest, Mocha, or Node's built-in test runner. Vitest is fully ESM compatible and recommended because Chai v6 (ESM) can no longer be fully provided in Jest runs — cds.test only provides a partial Chai emulation in Jest.
CDS 10+: New CAP projects are ESM-based by default (
"type": "module"inpackage.json). Useimportsyntax in test files. To create a CommonJS project, remove"type": "module"frompackage.json.
npm install --save-dev @cap-js/cds-test vitest
vitest.config.js:
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
testTimeout: 30000,
hookTimeout: 30000,
}
})
package.json:
{
"scripts": {
"test": "vitest run"
}
}
Basic integration test
// tests/catalog.test.js
import cds from '@sap/cds'
const { GET, POST, DELETE, expect } = cds.test('.')
describe('CatalogService', () => {
it('lists products', async () => {
const { data } = await GET('/catalog/Products')
expect(data.value).to.be.an('array')
expect(data.value.length).to.be.greaterThan(0)
})
it('rejects order with negative quantity', async () => {
const res = await POST('/catalog/submitOrder', {
product: '2b23bb4b-4ac7-4a24-ac02-aa10cabd842c',
quantity: -1
})
expect(res.status).to.equal(422)
})
})
Testing with authentication
it('admin can delete', async () => {
const { status } = await DELETE('/admin/Products(some-uuid)').as('alice')
expect(status).to.equal(204)
})
it('viewer cannot delete', async () => {
const { status } = await DELETE('/admin/Products(some-uuid)').as('carol')
expect(status).to.equal(403)
})
Mocking external services
// Vitest spyOn
const S4 = await cds.connect.to('S4')
vi.spyOn(S4, 'run').mockResolvedValue([{ BusinessPartner: 'BP001' }])
Testing custom actions
it('submitOrder returns success', async () => {
const { data, status } = await POST('/orders/submitOrder', {
orderID: 'some-uuid'
}).as('alice')
expect(status).to.equal(200)
expect(data.success).to.equal(true)
})
CSV test data
Put test data in db/data/. CAP loads these automatically in SQLite mode.
For test isolation, use in-memory SQLite:
{
"cds": {
"requires": {
"db": { "kind": "sqlite", "credentials": { "database": ":memory:" } }
}
}
}
Still on Jest? Known limitations
cds.test.expectis only a partial Chai emulation in Jest — not all Chai APIs work- Use
jest.spyOninstead ofvi.spyOn - Add
--forceExit:"test": "jest --forceExit" - Set
testTimeout: 30000injest.config.js - Plan migration to Vitest — CAP recommends migrating to Vitest, or using Jest's own
expectif you need to stick with Jest
Common mistakes to avoid
-
❌ Starting new CAP projects with Jest — Chai ESM issues mean incomplete assertion support
-
✅ Use Vitest for all new projects;
@cap-js/cds-testv1.0 is the stable recommended choice -
❌ Not setting
testTimeout— default 5s too short for CAP server startup -
✅ Set
testTimeout: 30000invitest.config.js -
❌ Testing with a shared HANA instance
-
✅ Use SQLite
:memory:for all unit and integration tests -
❌ Forgetting
.as('user')on protected endpoints — silent 401 failures -
✅ Always specify the mock user matching the required role
-
❌ Mocking
cds.dbdirectly -
✅ Use
vi.spyOn(srv, 'run')on the connected service instance