Skip to content

Commit 72e7fe2

Browse files
authored
docs(parameters): add "testing your code" section (#1383)
1 parent ec49023 commit 72e7fe2

10 files changed

+232
-16
lines changed

Diff for: docs/snippets/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
"@aws-sdk/client-secrets-manager": "^3.250.0",
3333
"@aws-sdk/client-ssm": "^3.245.0",
3434
"@aws-sdk/util-dynamodb": "^3.245.0",
35+
"aws-sdk-client-mock": "^2.0.1",
36+
"aws-sdk-client-mock-jest": "^2.0.1",
3537
"axios": "^1.2.4"
3638
}
37-
}
39+
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { clearCaches } from '@aws-lambda-powertools/parameters';
2+
3+
describe('Function tests', () => {
4+
5+
beforeEach(() => {
6+
jest.clearAllMocks();
7+
});
8+
9+
afterEach(() => {
10+
clearCaches();
11+
});
12+
13+
// ...
14+
15+
});
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { getSecret } from '@aws-lambda-powertools/parameters/secrets';
2+
3+
export const handler = async (_event: unknown, _context: unknown): Promise<Record<string, unknown>> => {
4+
try {
5+
const parameter = await getSecret('my-secret');
6+
7+
return {
8+
value: parameter
9+
};
10+
} catch (error) {
11+
return {
12+
message: 'Unable to retrieve secret',
13+
};
14+
}
15+
};
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { handler } from './testingYourCodeFunctionsHandler';
2+
import {
3+
SecretsManagerClient,
4+
GetSecretValueCommand,
5+
ResourceNotFoundException,
6+
} from '@aws-sdk/client-secrets-manager';
7+
import { mockClient } from 'aws-sdk-client-mock';
8+
import 'aws-sdk-client-mock-jest';
9+
10+
describe('Function tests', () => {
11+
12+
const client = mockClient(SecretsManagerClient);
13+
14+
beforeEach(() => {
15+
jest.clearAllMocks();
16+
});
17+
18+
afterEach(() => {
19+
client.reset();
20+
});
21+
22+
test('it returns the correct error message', async () => {
23+
24+
// Prepare
25+
client.on(GetSecretValueCommand)
26+
.rejectsOnce(new ResourceNotFoundException({
27+
$metadata: {
28+
httpStatusCode: 404,
29+
},
30+
message: 'Unable to retrieve secret',
31+
}));
32+
33+
// Act
34+
const result = await handler({}, {});
35+
36+
// Assess
37+
expect(result).toStrictEqual({ message: 'Unable to retrieve secret' });
38+
39+
});
40+
41+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { getParameter } from '@aws-lambda-powertools/parameters/ssm';
2+
3+
export const handler = async (_event: unknown, _context: unknown): Promise<Record<string, unknown>> => {
4+
const parameter = await getParameter('my/param');
5+
6+
return {
7+
value: parameter
8+
};
9+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { handler } from './testingYourCodeFunctionsHandler';
2+
import { getParameter } from '@aws-lambda-powertools/parameters/ssm';
3+
4+
jest.mock('@aws-lambda-powertools/parameters/ssm', () => ({
5+
getParameter: jest.fn()
6+
}));
7+
const mockedGetParameter = getParameter as jest.MockedFunction<typeof getParameter>;
8+
9+
describe('Function tests', () => {
10+
11+
beforeEach(() => {
12+
mockedGetParameter.mockClear();
13+
});
14+
15+
test('it returns the correct response', async () => {
16+
17+
// Prepare
18+
mockedGetParameter.mockResolvedValue('my/param');
19+
20+
// Act
21+
const result = await handler({}, {});
22+
23+
// Assess
24+
expect(result).toEqual({
25+
value: 'my/param',
26+
});
27+
28+
});
29+
30+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { AppConfigProvider } from '@aws-lambda-powertools/parameters/appconfig';
2+
3+
const provider = new AppConfigProvider({
4+
environment: 'dev',
5+
application: 'my-app',
6+
});
7+
8+
export const handler = async (_event: unknown, _context: unknown): Promise<Record<string, unknown>> => {
9+
const config = await provider.get('my-config');
10+
11+
return {
12+
value: config
13+
};
14+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { handler } from './testingYourCodeFunctionsHandler';
2+
import { AppConfigProvider } from '@aws-lambda-powertools/parameters/appconfig';
3+
4+
describe('Function tests', () => {
5+
6+
const providerSpy = jest.spyOn(AppConfigProvider.prototype, 'get');
7+
8+
beforeEach(() => {
9+
jest.clearAllMocks();
10+
});
11+
12+
test('it retrieves the config once and uses the correct name', async () => {
13+
14+
// Prepare
15+
const expectedConfig = {
16+
feature: {
17+
enabled: true,
18+
name: 'paywall',
19+
},
20+
};
21+
providerSpy.mockResolvedValueOnce(expectedConfig);
22+
23+
// Act
24+
const result = await handler({}, {});
25+
26+
// Assess
27+
expect(result).toStrictEqual({ value: expectedConfig });
28+
expect(providerSpy).toHaveBeenCalledTimes(1);
29+
expect(providerSpy).toHaveBeenCalledWith('my-config');
30+
31+
});
32+
33+
});

Diff for: docs/utilities/parameters.md

+55-2
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ By default, the provider will cache parameters retrieved in-memory for 5 seconds
134134

135135
You can adjust how long values should be kept in cache by using the param `maxAge`, when using `get()` or `getMultiple()` methods across all providers.
136136

137-
```typescript hl_lines="7 10" title="Caching parameters values in memory for longer than 5 seconds"
137+
```typescript hl_lines="7 11" title="Caching parameters values in memory for longer than 5 seconds"
138138
--8<-- "docs/snippets/parameters/adjustingCacheTTL.ts"
139139
```
140140

@@ -166,7 +166,7 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen
166166
| **decrypt** | `false` | Will automatically decrypt the parameter (see required [IAM Permissions](#iam-permissions)). |
167167
| **recursive** | `true` | For `getMultiple()` only, will fetch all parameter values recursively based on a path prefix. |
168168

169-
```typescript hl_lines="6 8" title="Example with get() and getMultiple()"
169+
```typescript hl_lines="6 9" title="Example with get() and getMultiple()"
170170
--8<-- "docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts"
171171
```
172172

@@ -397,3 +397,56 @@ The **`clientConfig`** parameter enables you to pass in a custom [config object]
397397
```typescript hl_lines="2 4-5"
398398
--8<-- "docs/snippets/parameters/clientConfig.ts"
399399
```
400+
401+
## Testing your code
402+
403+
### Mocking parameter values
404+
405+
For unit testing your applications, you can mock the calls to the parameters utility to avoid calling AWS APIs. This can be achieved in a number of ways - in this example, we use [Jest mock functions](https://jestjs.io/docs/es6-class-mocks#the-4-ways-to-create-an-es6-class-mock) to patch the `getParameters` function.
406+
407+
=== "handler.test.ts"
408+
```typescript hl_lines="2 4-7 12 18"
409+
--8<-- "docs/snippets/parameters/testingYourCodeFunctionsJestMock.ts"
410+
```
411+
412+
=== "handler.ts"
413+
```typescript
414+
--8<-- "docs/snippets/parameters/testingYourCodeFunctionsHandler.ts"
415+
```
416+
417+
With this pattern in place, you can customize the return values of the mocked function to test different scenarios without calling AWS APIs.
418+
419+
A similar pattern can be applied also to any of the built-in provider classes - in this other example, we use [Jest spyOn method](https://jestjs.io/docs/es6-class-mocks#mocking-a-specific-method-of-a-class) to patch the `get` function of the `AppConfigProvider` class. This is useful also when you want to test that the correct arguments are being passed to the Parameters utility.
420+
421+
=== "handler.test.ts"
422+
```typescript hl_lines="2 6 9 21"
423+
--8<-- "docs/snippets/parameters/testingYourCodeProvidersJestMock.ts"
424+
```
425+
426+
=== "handler.ts"
427+
```typescript
428+
--8<-- "docs/snippets/parameters/testingYourCodeProvidersHandler.ts"
429+
```
430+
431+
In some other cases, you might want to mock the AWS SDK v3 client itself, in these cases we recommend using the [`aws-sdk-client-mock`](https://www.npmjs.com/package/aws-sdk-client-mock) and [`aws-sdk-client-mock-jest`](https://www.npmjs.com/package/aws-sdk-client-mock-jest) libraries. This is useful when you want to test how your code behaves when the AWS SDK v3 client throws an error or a specific response.
432+
433+
=== "handler.test.ts"
434+
```typescript hl_lines="2-8 12 19 25-31"
435+
--8<-- "docs/snippets/parameters/testingYourCodeClientJestMock.ts"
436+
```
437+
438+
=== "handler.ts"
439+
```typescript
440+
--8<-- "docs/snippets/parameters/testingYourCodeClientHandler.ts"
441+
```
442+
443+
### Clearing cache
444+
445+
Parameters utility caches all parameter values for performance and cost reasons. However, this can have unintended interference in tests using the same parameter name.
446+
447+
Within your tests, you can use `clearCache` method available in [every provider](#built-in-provider-class). When using multiple providers or higher level functions like `getParameter`, use the `clearCaches` standalone function to clear cache globally.
448+
449+
=== "handler.test.ts"
450+
```typescript hl_lines="1 9-11"
451+
--8<-- "docs/snippets/parameters/testingYourCodeClearCache.ts"
452+
```

Diff for: package-lock.json

+17-13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)