Skip to main content

TableSpy

Enables spying the provided Table.

TableSpy is useful for writing unit tests, allowing you to stub sendable actions (e.g. Scans and Query), mock their behavior, and inspect their call history:

import { TableSpy } from 'dynamodb-toolbox/table/actions/spy'

const tableSpy = PokeTable.build(TableSpy)

// 🙌 Type-safe!
tableSpy.on(ScanCommand).resolve({ Items: mockedItems })

const { Items } = await PokeTable.build(ScanCommand)
.options({ consistent: true })
.send()

expect(Items).toStrictEqual(mockedItems) // ✅

const scanCount = tableSpy.sent(ScanCommand).count()
expect(scanCount).toBe(1) // ✅

// Reset history
tableSpy.reset()

// Stop spying
tableSpy.restore()
note

Non-mocked actions are sent as usual.

Methods

on(...)

(Action: SENDABLE_ACTION) => Stub<TABLE, SENDABLE_ACTION>

Enables stubbing a sendable action (see the stub section section for more details):

import { ScanCommand } from 'dynamodb-toolbox/table/actions/scan'

const scanStub = tableSpy.on(ScanCommand)

sent(...)

(Action: SENDABLE_ACTION) => Inspector<TABLE, SENDABLE_ACTION>

Enables inspecting a sendable action call history (see the inspector section section for more details):

import { ScanCommand } from 'dynamodb-toolbox/table/actions/scan'

const scanInspector = tableSpy.sent(ScanCommand)

reset()

() => Spy<TABLE>

Reset the call history for all actions:

expect(scanInspector.count()).toBe(1) // ✅

tableSpy.reset()

expect(scanInspector.count()).toBe(0) // ✅

// The method returns the spy, so you can chain a new stub:
tableSpy.reset().on(ScanCommand).resolve({ Items: [...] })

restore()

() => void

Stop spying the Table altogether:

// After this point, the spy is not able to intercept any action
tableSpy.restore()

Stub Methods

resolve(...)

(responseMock: Response<ACTION>) => Spy<TABLE>

Mocks the response of a sendable action .send() method:

// 🙌 Type-safe!
tableSpy.on(ScanCommand).resolve({ Items: mockedItems })

const { Items } = await PokeTable.build(ScanCommand).send()

expect(Items).toStrictEqual(mockedItems) // ✅

mock(...)

(mock: ((...args: Args<ACTION>) => Promisable<Response<ACTION>> | undefined)) => Spy<TABLE>

Mocks the implementation of a sendable action .send() method (synchronously or asynchronously), enabling you to return dynamic responses:

// 🙌 Type-safe!
tableSpy.on(ScanCommand).mock((entities, options) => {
if (
entities.length === 1 &&
entities[0] === PokemonEntity
) {
return { Items: mockedPokemons }
}
})

const { Items } = await PokeTable.build(ScanCommand)
.entities(PokemonEntity)
.send()

expect(Items).toStrictEqual(mockedPokemons) // ✅
info

Returning undefined is possible and lets the action proceed as usual.

reject(...)

(error?: string | Error | AwsError) => Spy<TABLE>

Simulates an error during the execution of a sendable action .send() method:

tableSpy.on(ScanCommand).reject()

await expect(() =>
PokeTable.build(ScanCommand).send()
).rejects.toThrow() // ✅
info

Stub methods return the original spy, so you can easily chain them:

tableSpy
.on(ScanCommand)
.resolve({ Items: [...] })
.on(QueryCommand)
.reject('Some error')

Inspector methods

count()

() => number

Returns the number of times the action was sent:

tableSpy.on(ScanCommand).resolve({ Items: mockedItems })

const { Items } = await PokeTable.build(ScanCommand).send()

const count = tableSpy.sent(ScanCommand).count()

expect(count).toBe(1) // ✅

allArgs()

() => Args<ACTION>[]

Returns the arguments of the sendable action call history:

tableSpy.on(ScanCommand).resolve({})

await PokeTable.build(ScanCommand)
.entities(PokemonEntity)
.options({ consistent: true })
.send()
await PokeTable.build(ScanCommand)
.entities(TrainerEntity)
.send()

const allArgs = tableSpy.sent(ScanCommand).allArgs()

expect(allArgs).toStrictEqual([
// First call
[PokemonEntity, { consistent: true }],
// Second call
[TrainerEntity, {}]
]) // ✅

args(...)

(index: number) => Args<ACTION>

Returns the arguments of the n-th action of the call history:

tableSpy.on(ScanCommand).resolve({})

await PokeTable.build(ScanCommand)
.entities(PokemonEntity)
.options({ consistent: true })
.send()
await PokeTable.build(ScanCommand)
.entities(TrainerEntity)
.send()

const firstArgs = tableSpy.sent(ScanCommand).args(0)

expect(firstArgs).toStrictEqual([
PokemonEntity,
{ consistent: true }
]) // ✅
note

Note that the index is zero-based.