I am trying to match the arguments of functions calls for my unit tests.
The test code is below:
beforeEach(() => {
enrollmentOffer = offerReadModel(EnrollmentOfferStatuses.Accepted, false, undefined, true)
fakeSearcher = buildFakeReadModelSearcher()
...
})
afterEach(() => {
sinon.restore()
})
it('does the right query to get the latest child offer', async () => {
await expect(calculateRenewInfo(enrollmentOffer as EnrollmentOfferReadModel, true)).to.be.fulfilled
expect(fakeSearcher.filter).to.have.been.calledWithExactly({
offerType: { eq: OfferTypes.Child },
enrollmentOfferId: { eq: enrollmentOffer.id },
})
})
Currently this unit-test errs with:
22) calculateRenewInfo
does the right query to get the latest child offer:
AssertionError: expected filter to have been called with exact arguments {
offerType: { eq: 'child' },
enrollmentOfferId: { eq: '0a1a6e82-c2ca-48ec-9548-c4b5e4d26653' }
}
{ enrollmentOfferId: { eq: '0a1a6e82-c2ca-48ec-9548-c4b5e4d26653' } } {
offerType: { eq: 'child' },
enrollmentOfferId: { eq: '0a1a6e82-c2ca-48ec-9548-c4b5e4d26653' }
}
The reason the test fails, I believe, is because there are two calls. The first internal invocation is matching my test arguments. While the second, with arguments being { enrollmentOfferId: { eq: '0a1a6e82-c2ca-48ec-9548-c4b5e4d26653' } }, is not matching it.
Normally, I would do something like: expect(sinonFake.firstCall).to.have.been.calledWith(...). This is however not working for me. For example, if I write expect(fakeSearcher.filter.firstCall).to.have.been.calledWithExactly(...), there is an error Property 'firstCall' does not exist on type '(filters: FilterFor<OfferReadModel>) => Searcher<OfferReadModel>'.
We build fakeSearcher with:
export function buildFakeReadModelSearcher<TReadModel = unknown>(): Partial<Searcher<TReadModel>> {
const fakeSearcher: Partial<Searcher<TReadModel>> = {}
fakeSearcher.filter = sinon.fake.returns(fakeSearcher)
fakeSearcher.afterCursor = sinon.fake.returns(fakeSearcher)
fakeSearcher.paginatedVersion = sinon.fake.returns(fakeSearcher)
fakeSearcher.limit = sinon.fake.returns(fakeSearcher)
fakeSearcher.sortBy = sinon.fake.returns(fakeSearcher)
fakeSearcher.search = sinon.fake.resolves({ items: [] })
fakeSearcher.searchOne = sinon.fake.resolves(undefined)
return fakeSearcher
}
Any suggestions/ideas how I could only match the first call, with no or minimal changes to fakeSearcher?
The problem is that
buildFakeReadModelSearchersays it returns aPartial<Searcher<TReadModel>>so Typescript doesn't know thatfakeSearcher.filteris a sinon fake.You could switch the function to return an object containing all those fakes + the fakeSearcher object as well and then use those fakes directly.