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()
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) // ✅
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:
- Any error
- Message
- AWS Error
tableSpy.on(ScanCommand).reject()
await expect(() =>
PokeTable.build(ScanCommand).send()
).rejects.toThrow() // ✅
tableSpy.on(ScanCommand).reject('Fake error')
await expect(() =>
PokeTable.build(ScanCommand).send()
).rejects.toThrow('Fake error') // ✅
tableSpy.on(ScanCommand).reject({
Name: 'ServiceUnavailable',
Code: '503',
Message: 'Service is unable to handle request.',
$fault: 'server',
$service: 'DynamoDB'
})
await expect(() =>
PokeTable.build(ScanCommand).send()
).rejects.toThrow({ Name: 'ServiceUnavailable' }) // ✅
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 that the index is zero-based.