Skip to main content

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()
note

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) // ✅
info

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:

entitySpy.on(GetItemCommand).reject()

await expect(() =>
PokemonEntity.build(GetItemCommand).key(key).send()
).rejects.toThrow() // ✅
info

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

Note that the index is zero-based.