lambda-api
lambda-api copied to clipboard
Question - Unit Testing: Mocking Request and Response Objects
When unit testing my handler functions with Jest (although I doubt this issue is specific to Jest), with the signature of somefunction(req, res), I'd like to test specific behavior my application applies on req and res.
For example:
return promise
.then((data) => {
return res.status(200).json(data);
})
.catch(error => res.status(500).json(error));;
I'd like to assert that, in the event of an error, res.status was called with 500 and res.status().json was called with the details of the error.
While it's possible to do this by mocking implementations that return other mocks, I'm finding it a bit tedious and wondering if anyone else has found an easy way to do this.
Is there perhaps a way to access the base Request and Response classes, and use them to autogenerate mocks?
Is there a particular reason you want to test that status() was called, as opposed to looking at the status code in the response?
For example, my tests for the routing usually look along these lines:
it('returns a 404 if batch ID does not exist', async () => {
batches.getById.mockResolvedValueOnce(undefined)
const { body, statusCode } = await testApi(makeHttpEvent({
'path': `${API_BASE_PATH}/batches/somethingThatDoesntExist`,
'pathParameters': {
'id': 'somethingThatDoesntExist',
},
}))
expect(statusCode).toBe(404)
expect(body).toBe('Not Found')
})
The two helpers are:
testApiWrapsapi.run(event)in a Promise that parses the returned JSONmakeHttpEventhas a default fixture for what a complete Lambda event object looks like, and overrides any properties that are present in the argument object
Personally I find that this makes for concise and readable API tests.
@Sleavely I realise this is an old issue, but where are you getting makeHttpEvent from? Is that a custom method you made, or something provided by a library?
@lukemcgregor It's a custom function. I don't have access to verbatim examples at the moment but basically it has a default Lambda event object such as this one https://gist.github.com/anuradhawick/6fffd792ebae3148c11ebecf6b966b2a and then merges that with the input so that you can create mock events without too much copy-pasta.
Something along these lines:
// This is just an example, refer to the link from earlier if you want something that looks like a real event
const defaultEvent = {
httpMethod: 'POST',
path: '/',
resource: '/{proxy+}',
queryStringParameters: {}
headers: {},
requestContext: {},
pathParameters: {},
stageVariables: {},
isBase64Encoded: false,
body: {},
}
const makeHttpEvent = (input) => {
// In reality you may want a recursive (deep) merge instead
return { ...defaultEvent, ...input }
}
Now in order to fake a POST request to /checkout you only need to do something like
const myEventPayload = makeHttpEvent({ path: '/checkout' })
api.run(myEventPayload)
Hey @MattCopenhaver, thanks for opening this issue and sorry for the delayed response.
I agree with @Sleavely. Closing this issue for now, feel free to ping me if you need anything 🙏