Open gagaXD opened 5 years ago
Hi @gagaXD, thanks so much for starting this discussion! For all the years I've used AWS, I've never used the aws-sdk-mock
. My coding style is such that I never use module mocking; only mocking of individual functions. That said, it looks like it would be easy for me to create a @sullux/aws-sdk-mock
project, or alternately to add a /mock
extension to this existing project. I will look into it and will advise you when it's ready -- hopefully it should be ready by tomorrow if there's not some hidden complexity.
As an aside, I'll share the way I do my testing without module mocking since I imagine some readers of this thread will be asking that question. I am a very functional programmer, so I tend to keep my functions pure by passing IO dependencies (such the AWS SDK functions) to my testable functions; thus, my unit tests just need to pass mocked functions. Here's an example project based on your above dynamo example. I also chose to use Date.now()
to illustrate how I keep my testable functions pure. (I kept this code imperative to make it easier to read; normally I write more functionally.)
- /workflow
--- index.js
--- workflow.js
--- workflow.spec.js
NOT TESTED. This module simply injects runtime dependencies into the pure functions and surfaces them for the rest of the app. This file will only be tested by integration tests; not unit tests.
const { dynamoDB: { documentClient } } = require('@sullux/aws-sdk')()
const { workflow, NOT_FOUND_ERROR } = require('./workflow')
const { get: getItem } = documentClient
// pull together the non-deterministic dependencies
const dependencies = {
getItem,
now: Date.now,
tableName: process.env.WORKFLOW_TABLE_NAME,
}
// inject the dependencies into the factory to get the full implementation
const { getWorkflow } = workflow(dependencies)
// export the runtime implementation functions
module.exports = {
getWorkflow,
NOT_FOUND_ERROR,
}
This is the implementation file. It uses only pure functions and is fully unit-testable. The rest of the app does not use this file directly; these exports are used from the index.js
file.
const NOT_FOUND_ERROR = 'ERR_NOT_FOUND'
const workflow = ({
getItem,
now,
tableName,
}) => ({
getWorkflow: async (workflowName) => {
const params = {
TableName: tableName,
Key: { workflowName },
}
const { Item: result } = await getItem(params)
if (!result) {
const error = new Error(`Workflow "${workflowName}" was not found.`)
error.code = NOT_FOUND_ERROR
throw error
}
return { result, timestamp: now() }
},
})
module.exports = {
workflow,
NOT_FOUND_ERROR,
}
Using Jest:
const { workflow, NOT_FOUND_ERROR } = require('./workflow')
describe('workflow', () => {
const mockDependencies = () => ({
{
getItem: jest.fn(() => Promise.resolve({ Item: expectedResult.result }))
now: () => 11111
tableName: 'workflow-table'
}
})
test('should query the workflow table', async () => {
const dependencies = mockDependencies()
const { getWorkflow } = workflow(dependencies)
await getWorkflow('workflow1')
expect(dependencies.getItem.mock.calls)
.toEqual([[{ TableName: 'workflow-table', Key: { workflowName: 'workflow1' } }]])
})
test('should return the workflow and timestamp', () => {
const dependencies = mockDependencies()
const { getWorkflow } = workflow(dependencies)
const result = await getWorkflow('workflow1')
const expectedResult = { result: { foo: 42 }, timestamp: 11111 }
expect(result).toEqual(expectedResult)
})
test('should reject when not found', () => {
const dependencies = {
...mockDependencies(),
getItem: jest.fn(() => Promise.resolve({}))
}
const { getWorkflow } = workflow(dependencies)
const error = await getWorkflow('workflow1').catch(error => error)
expect(error).toBeDefined()
expect(error.code).toEqual(NOT_FOUND_ERROR)
})
})
I haven't run any of the above code -- just jotted it down quickly as an example of how I work. To consume this in the rest of the app, simply require the folder as in require('./workflow')
. That way you will be loading the runtime implementations from the index file.
Je voix que t'abites a Paris -- je n'ecrit pas bient du tout mais je suis fiere de la faite que je parles francais assez courement. 🙂
I will let you know as soon as I have a mock SDK available.
Update: I looked into it, and it seems that aws-sdk-mock
is a thin utility to allow sinon mocking of legacy aws-sdk
. It will take some extra effort for me to mimic that functionality directly. I just pushed an update that memoizes sdk instances in an attempt to allow mocking, but apparently sinon doesn't natively support overwriting read-only properties (it isn't hard -- they just haven't chosen to do it).
As a consequence, I will need to add my own feature to allow mocking. I will try to make it follow the pattern of:
const mockS3 = aws().s3.mock()
My recent memoization change will allow this to work consistently between the test file and the implementation file. I was hoping the mocking would be an easy modification, but sinon wasn't cooperative so I will need another day or two to get this ready. I will keep updating this thread.
Hello !
First of all, let me say thank you, your wrapper really solved many of my issues with
aws-sdk
:rocket:I'm trying to add some tests to my lambdas.
In order to do that, I'm using aws-sdk-mock.
Official SDK
It's working great if I use the official aws-sdk :
File.js
and in my test (using jest)
Sullux SDK
But, if instead of using the official aws-sdk, i'm using
@sullux/aws-sdk
, the mock is not working, and I get errors from aws (which should not be called if the mock was working)( example with S3)
bucket.js
in my test
I got an error from AWS
Error: Error calling AWS.S3.getObject: Access Denied
I was wondering if you had any idea how I could get this to work. I've tried to use the
setSDKInstance
ofaws-sdk-mock
, but no luckThanks again for your work !