EntitySpy
Enables spying the provided Entity
.
EntitySpy
is useful for writing unit tests, allowing you to stub sendable actions (e.g. GetItemCommand
, PutItemCommand
etc.), mock their behavior, and inspect their call history:
import { EntitySpy } from 'dynamodb-toolbox/entity/actions/spy'
const entitySpy = PokemonEntity.build(EntitySpy)
// 🙌 Type-safe!
entitySpy.on(GetItemCommand).resolve({ Item: pokeMock })
const { Item } = await PokemonEntity.build(GetItemCommand)
.key(key)
.options({ consistent: true })
.send()
expect(Item).toStrictEqual(pokeMock) // ✅
const getCount = entitySpy.sent(GetItemCommand).count()
expect(getCount).toBe(1) // ✅
// Reset history
entitySpy.reset()
// Stop spying
entitySpy.restore()
Non-mocked actions are sent as usual.
Methods
on(...)
(Action: SENDABLE_ACTION) => Stub<ENTITY, SENDABLE_ACTION>
Enables stubbing a sendable action (see the stub section section for more details):
import { GetItemCommand } from 'dynamodb-toolbox/entity/actions/get'
const getStub = entitySpy.on(GetItemCommand)
sent(...)
(Action: SENDABLE_ACTION) => Inspector<ENTITY, SENDABLE_ACTION>
Enables inspecting a sendable action call history (see the inspector section section for more details):
import { GetItemCommand } from 'dynamodb-toolbox/entity/actions/get'
const getInspector = entitySpy.sent(GetItemCommand)
reset()
() => Spy<ENTITY>
Reset the call history for all actions:
expect(getInspector.count()).toBe(1) // ✅
entitySpy.reset()
expect(getInspector.count()).toBe(0) // ✅
// The method returns the spy, so you can chain a new stub:
entitySpy.reset().on(GetItemCommand).resolve({ Item: ... })
restore()
() => void
Stop spying the Entity
altogether:
// After this point, the spy is not able to intercept any action
entitySpy.restore()
Stub Methods
resolve(...)
(responseMock: Response<ACTION>) => Spy<ENTITY>
Mocks the response of a sendable action .send()
method:
// 🙌 Type-safe!
entitySpy.on(GetItemCommand).resolve({ Item: pokeMock })
const { Item } = await PokemonEntity.build(GetItemCommand)
.key(key)
.send()
expect(Item).toStrictEqual(pokeMock) // ✅
mock(...)
(mock: ((...args: Args<ACTION>) => Promisable<Response<ACTION>> | undefined)) => Spy<ENTITY>
Mocks the implementation of a sendable action .send()
method (synchronously or asynchronously), enabling you to return dynamic responses:
// 🙌 Type-safe!
entitySpy.on(GetItemCommand).mock((key, options) => {
if (key.pokemonId === 'pikachu') {
return { Item: pikachuMock }
}
})
const { Item } = await PokemonEntity.build(GetItemCommand)
.key({ pokemonId: 'pikachu' })
.send()
expect(Item).toStrictEqual(pikachuMock) // ✅
Returning undefined
is possible and lets the action proceed as usual.
reject(...)
(error?: string | Error | AwsError) => Spy<ENTITY>
Simulates an error during the execution of a sendable action .send()
method:
- Any error
- Message
- AWS Error
entitySpy.on(GetItemCommand).reject()
await expect(() =>
PokemonEntity.build(GetItemCommand).key(key).send()
).rejects.toThrow() // ✅
entitySpy.on(GetItemCommand).reject('Fake error')
await expect(() =>
PokemonEntity.build(GetItemCommand).key(key).send()
).rejects.toThrow('Fake error') // ✅
entitySpy.on(GetItemCommand).reject({
Name: 'ServiceUnavailable',
Code: '503',
Message: 'Service is unable to handle request.',
$fault: 'server',
$service: 'DynamoDB'
})
await expect(() =>
PokemonEntity.build(GetItemCommand).key(key).send()
).rejects.toThrow({ Name: 'ServiceUnavailable' }) // ✅
Stub methods return the original spy, so you can easily chain them:
entitySpy
.on(GetItemCommand)
.resolve({ Item: ... })
.on(PutItemCommand)
.reject('Some error')
Inspector methods
count()
() => number
Returns the number of times the action was sent:
entitySpy.on(GetItemCommand).resolve({ Item: pokeMock })
const { Item } =
await PokemonEntity.build(GetItemCommand).send()
const count = entitySpy.sent(GetItemCommand).count()
expect(count).toBe(1) // ✅
allArgs()
() => Args<ACTION>[]
Returns the arguments of the sendable action call history:
entitySpy.on(GetItemCommand).resolve({})
await PokemonEntity.build(GetItemCommand)
.key({ pokemonId: 'pikachu' })
.options({ consistent: true })
.send()
await PokemonEntity.build(GetItemCommand)
.key({ pokemonId: 'charizard' })
.send()
const allArgs = entitySpy.sent(GetItemCommand).allArgs()
expect(allArgs).toStrictEqual([
// First call
[{ pokemonId: 'pikachu' }, { consistent: true }],
// Second call
[{ pokemoneId: 'charizard' }, {}]
]) // ✅
args(...)
(index: number) => Args<ACTION>
Returns the arguments of the n-th action of the call history:
entitySpy.on(GetItemCommand).resolve({})
await PokemonEntity.build(GetItemCommand)
.key({ pokemonId: 'pikachu' })
.options({ consistent: true })
.send()
await PokemonEntity.build(GetItemCommand)
.key({ pokemonId: 'charizard' })
.send()
const firstArgs = entitySpy.sent(GetItemCommand).args(0)
expect(firstArgs).toStrictEqual([
{ pokemonId: 'pikachu' },
{ consistent: true }
]) // ✅
Note that the index is zero-based.