Mocking modular Amazon SDK for JavaScript (v3) in Unit Tests

JavaScript
TypeScript
海外精选
海外精选的内容汇集了全球优质的亚马逊云科技相关技术内容。同时,内容中提到的“AWS” 是 “Amazon Web Services” 的缩写,在此网站不作为商标展示。
0
0
{"value":"The Amazon SDK for Javascript team would like to highlight the open-source community and it’s contributions. Today we welcome a guest blog written by [Maciej Radzikowski](https://twitter.com/radzikowski_m) on [aws-sdk-client-mock](https://github.com/m-radzikowski/aws-sdk-client-mock), a library that allows easy mocking of [AWS SDK for JavaScript (v3)](https://github.com/aws/aws-sdk-js-v3/).\n\nOn December 15th, 2020, AWS announced the [general availability of the AWS SDK for JavaScript, version 3 (v3)](https://aws.amazon.com/cn/blogs/developer/modular-aws-sdk-for-javascript-is-now-generally-available/). In order to test your code behavior thoroughly when using the SDK, it must be easy to mock in unit tests. This blog post shows how you can mock the SDK clients using the community-driven [AWS SDK Client mock](https://github.com/m-radzikowski/aws-sdk-client-mock) library.\n\n#### **Background**\n\nSpending too much time on building mocks instead of writing unit tests slows down the development process.\n\nFor the AWS SDK for JavaScript (v2), you can use the [aws-sdk-mock](https://github.com/dwyl/aws-sdk-mock) library for unit test mocks. It is built by the community and allows you to execute your custom logic for the SDK method calls. I needed something similar for the AWS SDK for JavaScript (v3).\n\nI wanted to switch to the Amazon SDK for JavaScript (v3) from the moment it became generally available, and retain the high unit tests coverage at the same time. That’s why I created the AWS [SDK Client mock](https://github.com/m-radzikowski/aws-sdk-client-mock) which is a specialized solution to mock AWS SDK for JavaScript (v3) in a unified, straightforward, and readable way.\n\n#### **Usage**\n\nThe mocking library for AWS SDK for JavaScript (v3) can be used in any JavaScript unit testing framework. Internally, it uses [Sinon.JS](https://sinonjs.org/) to create robust stubs in place of the SDK calls. You can install it with your favorite package manager.\n\nIn the following code, we use npm:\n\n```\nBash\n```\n```\nnpm install --save-dev aws-sdk-client-mock\n```\n\n\n\n#### **Example code to be tested**\n\nWe have a function that takes an array of user IDs, finds the user information for each user ID from DynamoDB using [DynamoDB Document Client](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/dynamodb-example-dynamodb-utilities.html), and returns an array of corresponding user names:\n```\nTypeScript\n```\n```\n// getUserNames.ts\nimport { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";\nimport { DynamoDBDocumentClient, GetCommand } from \"@aws-sdk/lib-dynamodb\";\n\nexport const getUserNames = async (userIds: string[]) => {\n const dynamodb = new DynamoDBClient({});\n const ddb = DynamoDBDocumentClient.from(dynamodb);\n const names = [];\n for (const userId of userIds) {\n const result = await ddb.send(\n new GetCommand({\n TableName: \"users\",\n Key: {\n id: userId,\n },\n })\n );\n names.push(result.Item?.name);\n }\n return names;\n};\n```\n\n\nWe can optimize this code using a ```BatchGetCommand```instead of the ```GetCommand```.But we keep it short and simple to focus on our tests.\n\n#### **Example code for unit testing**\n\nAll the examples below use [Jest](https://jestjs.io/) as a testing framework. The mocks and presented ideas will work the same way in any other testing framework.\n\nIn your unit test file, you need to import```aws-sdk-client-mock``` and the client to be tested. In the below example, we import DynamoDB Document Client.\n\n```\nTypeScript\n```\n```\nimport { mockClient } from \"aws-sdk-client-mock\";\nimport { DynamoDBDocumentClient } from \"@aws-sdk/lib-dynamodb\";\n```\n\n\nIn Amazon SDK for JavaScript (v3), all calls to operations are made using the Client class objects. You need to pass the class of the client you want to mock. We start with creating the mock instance as follows:\n\n```\nTypeScript\n```\n```\nconst ddbMock = mockClient(DynamoDBDocumentClient);\n```\n\n\nAfter every test execution, you need to reset the history and behavior of the mock. Otherwise, the tests could interfere with each other. You can achieve this by resetting the mock before each individual test:\n\n```\nTypeScript\n```\n```\nbeforeEach(() => {\n ddbMock.reset();\n});\n```\n\n\nNow you write your first test as follows:\n\n```\nTypeScript\n```\n```\n// getUserNames.spec.ts\nimport { getUserNames } from \"./getUserNames\";\nimport { GetCommand } from \"@aws-sdk/lib-dynamodb\";\n\nit(\"should get user names from the DynamoDB\", async () => {\n ddbMock.on(GetCommand).resolves({\n Item: { id: \"user1\", name: \"John\" },\n });\n const names = await getUserNames([\"user1\"]);\n expect(names).toStrictEqual([\"John\"]);\n});\n```\n\n\nIn the first line of the unit test, we specify the mock behavior. When called with the ```GetCommand```, it will resolve with the specified object containing our user data. Then we call the function under test and check if the response matches the value returned from the mock.\n\nIn the above example, the mock returns a user object for any ```GetCommand``` call. We can expand the test by implementing different responses depending on command parameters as follows:\n\n```\nTypeScript\n```\n```\nimport { GetCommand } from \"@aws-sdk/lib-dynamodb\";\n\nit(\"should get user names from the DynamoDB\", async () => {\n ddbMock\n .on(GetCommand)\n .resolves({\n Item: undefined,\n })\n .on(GetCommand, {\n TableName: \"users\",\n Key: { id: \"user1\" },\n })\n .resolves({\n Item: { id: \"user1\", name: \"Alice\" },\n })\n .on(GetCommand, {\n TableName: \"users\",\n Key: { id: \"user2\" },\n })\n .resolves({\n Item: { id: \"user2\", name: \"Bob\" },\n });\n const names = await getUserNames([\"user1\", \"user2\", \"user3\"]);\n expect(names).toStrictEqual([\"Alice\", \"Bob\", undefined]);\n});\n```\n\nIn the unit test above, we define the default behavior for the ```GetCommand```operation, resolving with an undefined value for ```Item```. After that, we defined more specific behaviors in which the name```Alice``` is returned for user ID ```user1```and the name ```Bob```is returned for user ID ```user2```.\n\nYou can git clone [aws-samples/aws-sdk-js-client-mock-tes](https://github.com/aws-samples/aws-sdk-js-client-mock-test)t and run these unit tests in your environment.\n\n#### **Built-in type checking**\n\nJust like Amazon SDK for JavaScript (v3), the mocks are also fully typed. When using TypeScript, the output object you provide is statically checked against the expected output type for the```GetCommand```.\n\nFor example, update the mock to return boolean true instead of Item object as follows:\n\n```\nTypeScript\n```\nddbMock.on(GetCommand).resolves(true);\n\nTypeScript will throw the following error informing you that a boolean value can’t be returned as a output from```\nGetCommand```:\n\n```\nBash\n```\nArgument of type 'boolean' is not assignable to parameter of\ntype 'CommandResponse<GetCommandOutput>'.\n\nThe types in the unit test mocks help you write unit tests faster thanks to code suggestions and completion in your IDE and avoid mistakes like typos in the property names.\n\n#### **Further reading**\n\nThe mocking library also supports mocking for more complex operations, like:\n\n- Specifying reject results (errors) from Client calls.\n- Providing a custom function invoked for the incoming calls.\n- Inspecting received calls instead of returning the predeclared response.\n\nYou can find all of them described with examples in the repository and [API reference](https://m-radzikowski.github.io/aws-sdk-client-mock/).\n\n#### **Community and Development**\n\nThe AWS SDK Client mock library is an open-source project under the MIT license. It is compatible with all the AWS SDK for JavaScript (v3) Clients. As it’s community-driven, there are several ways you can help:\n\n- Star it on GitHub to spread the word.\n- Create and/or vote on [issues](https://github.com/m-radzikowski/aws-sdk-client-mock/issues).\n- Submit pull requests.\n\ns of now, the aws-sdk-client-mock library enables mocking of only the most essential SDK operation, i.e. sending Commands. Other capabilities and improvements will likely be added over time. Upon stumbling on the library, the AWS SDK for JavaScript team provided help to make it work seamlessly with the SDK itself and I’m very thankful for that.\n\n#### **Conclusion**\n\nThe AWS SDK for JavaScript team recommends AWS SDK Client mock library for mocking the AWS SDK for JavaScript (v3). Give it a try to make your code reliable, keeping your tests straightforward and easily readable. If you have any feedback, bug reports, or feature requests, please open an [issue on GitHub](https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fm-radzikowski%2Faws-sdk-client-mock%2Fissues%2Fnew).\n\n![image.png](https://dev-media.amazoncloud.cn/f05b644abcf44cce8f167666958bffc2_image.png)\n[ **Maciej Radzikowski**](https://twitter.com/radzikowski_m)\n\nMaciej is a software developer at [Merapar](https://merapar.com/) focused on AWS serverless solutions and sharing knowledge. He writes programming and serverless guides on his blog at [BetterDev.blog](https://betterdev.blog/) and shorter tips on Twitter [@radzikowski_m](https://twitter.com/radzikowski_m)\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n ","render":"<p>The Amazon SDK for Javascript team would like to highlight the open-source community and it’s contributions. Today we welcome a guest blog written by <a href=\"https://twitter.com/radzikowski_m\" target=\"_blank\">Maciej Radzikowski</a> on <a href=\"https://github.com/m-radzikowski/aws-sdk-client-mock\" target=\"_blank\">aws-sdk-client-mock</a>, a library that allows easy mocking of <a href=\"https://github.com/aws/aws-sdk-js-v3/\" target=\"_blank\">AWS SDK for JavaScript (v3)</a>.</p>\n<p>On December 15th, 2020, AWS announced the <a href=\"https://aws.amazon.com/cn/blogs/developer/modular-aws-sdk-for-javascript-is-now-generally-available/\" target=\"_blank\">general availability of the AWS SDK for JavaScript, version 3 (v3)</a>. In order to test your code behavior thoroughly when using the SDK, it must be easy to mock in unit tests. This blog post shows how you can mock the SDK clients using the community-driven <a href=\"https://github.com/m-radzikowski/aws-sdk-client-mock\" target=\"_blank\">AWS SDK Client mock</a> library.</p>\n<h4><a id=\"Background_4\"></a><strong>Background</strong></h4>\n<p>Spending too much time on building mocks instead of writing unit tests slows down the development process.</p>\n<p>For the AWS SDK for JavaScript (v2), you can use the <a href=\"https://github.com/dwyl/aws-sdk-mock\" target=\"_blank\">aws-sdk-mock</a> library for unit test mocks. It is built by the community and allows you to execute your custom logic for the SDK method calls. I needed something similar for the AWS SDK for JavaScript (v3).</p>\n<p>I wanted to switch to the Amazon SDK for JavaScript (v3) from the moment it became generally available, and retain the high unit tests coverage at the same time. That’s why I created the AWS <a href=\"https://github.com/m-radzikowski/aws-sdk-client-mock\" target=\"_blank\">SDK Client mock</a> which is a specialized solution to mock AWS SDK for JavaScript (v3) in a unified, straightforward, and readable way.</p>\n<h4><a id=\"Usage_12\"></a><strong>Usage</strong></h4>\n<p>The mocking library for AWS SDK for JavaScript (v3) can be used in any JavaScript unit testing framework. Internally, it uses <a href=\"https://sinonjs.org/\" target=\"_blank\">Sinon.JS</a> to create robust stubs in place of the SDK calls. You can install it with your favorite package manager.</p>\n<p>In the following code, we use npm:</p>\n<pre><code class=\"lang-\">Bash\n</code></pre>\n<pre><code class=\"lang-\">npm install --save-dev aws-sdk-client-mock\n</code></pre>\n<h4><a id=\"Example_code_to_be_tested_27\"></a><strong>Example code to be tested</strong></h4>\n<p>We have a function that takes an array of user IDs, finds the user information for each user ID from DynamoDB using <a href=\"https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/dynamodb-example-dynamodb-utilities.html\" target=\"_blank\">DynamoDB Document Client</a>, and returns an array of corresponding user names:</p>\n<pre><code class=\"lang-\">TypeScript\n</code></pre>\n<pre><code class=\"lang-\">// getUserNames.ts\nimport { DynamoDBClient } from &quot;@aws-sdk/client-dynamodb&quot;;\nimport { DynamoDBDocumentClient, GetCommand } from &quot;@aws-sdk/lib-dynamodb&quot;;\n\nexport const getUserNames = async (userIds: string[]) =&gt; {\n const dynamodb = new DynamoDBClient({});\n const ddb = DynamoDBDocumentClient.from(dynamodb);\n const names = [];\n for (const userId of userIds) {\n const result = await ddb.send(\n new GetCommand({\n TableName: &quot;users&quot;,\n Key: {\n id: userId,\n },\n })\n );\n names.push(result.Item?.name);\n }\n return names;\n};\n</code></pre>\n<p>We can optimize this code using a <code>BatchGetCommand</code>instead of the <code>GetCommand</code>.But we keep it short and simple to focus on our tests.</p>\n<h4><a id=\"Example_code_for_unit_testing_60\"></a><strong>Example code for unit testing</strong></h4>\n<p>All the examples below use <a href=\"https://jestjs.io/\" target=\"_blank\">Jest</a> as a testing framework. The mocks and presented ideas will work the same way in any other testing framework.</p>\n<p>In your unit test file, you need to import<code>aws-sdk-client-mock</code> and the client to be tested. In the below example, we import DynamoDB Document Client.</p>\n<pre><code class=\"lang-\">TypeScript\n</code></pre>\n<pre><code class=\"lang-\">import { mockClient } from &quot;aws-sdk-client-mock&quot;;\nimport { DynamoDBDocumentClient } from &quot;@aws-sdk/lib-dynamodb&quot;;\n</code></pre>\n<p>In Amazon SDK for JavaScript (v3), all calls to operations are made using the Client class objects. You need to pass the class of the client you want to mock. We start with creating the mock instance as follows:</p>\n<pre><code class=\"lang-\">TypeScript\n</code></pre>\n<pre><code class=\"lang-\">const ddbMock = mockClient(DynamoDBDocumentClient);\n</code></pre>\n<p>After every test execution, you need to reset the history and behavior of the mock. Otherwise, the tests could interfere with each other. You can achieve this by resetting the mock before each individual test:</p>\n<pre><code class=\"lang-\">TypeScript\n</code></pre>\n<pre><code class=\"lang-\">beforeEach(() =&gt; {\n ddbMock.reset();\n});\n</code></pre>\n<p>Now you write your first test as follows:</p>\n<pre><code class=\"lang-\">TypeScript\n</code></pre>\n<pre><code class=\"lang-\">// getUserNames.spec.ts\nimport { getUserNames } from &quot;./getUserNames&quot;;\nimport { GetCommand } from &quot;@aws-sdk/lib-dynamodb&quot;;\n\nit(&quot;should get user names from the DynamoDB&quot;, async () =&gt; {\n ddbMock.on(GetCommand).resolves({\n Item: { id: &quot;user1&quot;, name: &quot;John&quot; },\n });\n const names = await getUserNames([&quot;user1&quot;]);\n expect(names).toStrictEqual([&quot;John&quot;]);\n});\n</code></pre>\n<p>In the first line of the unit test, we specify the mock behavior. When called with the <code>GetCommand</code>, it will resolve with the specified object containing our user data. Then we call the function under test and check if the response matches the value returned from the mock.</p>\n<p>In the above example, the mock returns a user object for any <code>GetCommand</code> call. We can expand the test by implementing different responses depending on command parameters as follows:</p>\n<pre><code class=\"lang-\">TypeScript\n</code></pre>\n<pre><code class=\"lang-\">import { GetCommand } from &quot;@aws-sdk/lib-dynamodb&quot;;\n\nit(&quot;should get user names from the DynamoDB&quot;, async () =&gt; {\n ddbMock\n .on(GetCommand)\n .resolves({\n Item: undefined,\n })\n .on(GetCommand, {\n TableName: &quot;users&quot;,\n Key: { id: &quot;user1&quot; },\n })\n .resolves({\n Item: { id: &quot;user1&quot;, name: &quot;Alice&quot; },\n })\n .on(GetCommand, {\n TableName: &quot;users&quot;,\n Key: { id: &quot;user2&quot; },\n })\n .resolves({\n Item: { id: &quot;user2&quot;, name: &quot;Bob&quot; },\n });\n const names = await getUserNames([&quot;user1&quot;, &quot;user2&quot;, &quot;user3&quot;]);\n expect(names).toStrictEqual([&quot;Alice&quot;, &quot;Bob&quot;, undefined]);\n});\n</code></pre>\n<p>In the unit test above, we define the default behavior for the <code>GetCommand</code>operation, resolving with an undefined value for <code>Item</code>. After that, we defined more specific behaviors in which the name<code>Alice</code> is returned for user ID <code>user1</code>and the name <code>Bob</code>is returned for user ID <code>user2</code>.</p>\n<p>You can git clone <a href=\"https://github.com/aws-samples/aws-sdk-js-client-mock-test\" target=\"_blank\">aws-samples/aws-sdk-js-client-mock-tes</a>t and run these unit tests in your environment.</p>\n<h4><a id=\"Builtin_type_checking_156\"></a><strong>Built-in type checking</strong></h4>\n<p>Just like Amazon SDK for JavaScript (v3), the mocks are also fully typed. When using TypeScript, the output object you provide is statically checked against the expected output type for the<code>GetCommand</code>.</p>\n<p>For example, update the mock to return boolean true instead of Item object as follows:</p>\n<pre><code class=\"lang-\">TypeScript\n</code></pre>\n<p>ddbMock.on(GetCommand).resolves(true);</p>\n<p>TypeScript will throw the following error informing you that a boolean value can’t be returned as a output from<code> GetCommand</code>:</p>\n<pre><code class=\"lang-\">Bash\n</code></pre>\n<p>Argument of type ‘boolean’ is not assignable to parameter of<br />\ntype ‘CommandResponse&lt;GetCommandOutput&gt;’.</p>\n<p>The types in the unit test mocks help you write unit tests faster thanks to code suggestions and completion in your IDE and avoid mistakes like typos in the property names.</p>\n<h4><a id=\"Further_reading_178\"></a><strong>Further reading</strong></h4>\n<p>The mocking library also supports mocking for more complex operations, like:</p>\n<ul>\n<li>Specifying reject results (errors) from Client calls.</li>\n<li>Providing a custom function invoked for the incoming calls.</li>\n<li>Inspecting received calls instead of returning the predeclared response.</li>\n</ul>\n<p>You can find all of them described with examples in the repository and <a href=\"https://m-radzikowski.github.io/aws-sdk-client-mock/\" target=\"_blank\">API reference</a>.</p>\n<h4><a id=\"Community_and_Development_188\"></a><strong>Community and Development</strong></h4>\n<p>The AWS SDK Client mock library is an open-source project under the MIT license. It is compatible with all the AWS SDK for JavaScript (v3) Clients. As it’s community-driven, there are several ways you can help:</p>\n<ul>\n<li>Star it on GitHub to spread the word.</li>\n<li>Create and/or vote on <a href=\"https://github.com/m-radzikowski/aws-sdk-client-mock/issues\" target=\"_blank\">issues</a>.</li>\n<li>Submit pull requests.</li>\n</ul>\n<p>s of now, the aws-sdk-client-mock library enables mocking of only the most essential SDK operation, i.e. sending Commands. Other capabilities and improvements will likely be added over time. Upon stumbling on the library, the AWS SDK for JavaScript team provided help to make it work seamlessly with the SDK itself and I’m very thankful for that.</p>\n<h4><a id=\"Conclusion_198\"></a><strong>Conclusion</strong></h4>\n<p>The AWS SDK for JavaScript team recommends AWS SDK Client mock library for mocking the AWS SDK for JavaScript (v3). Give it a try to make your code reliable, keeping your tests straightforward and easily readable. If you have any feedback, bug reports, or feature requests, please open an <a href=\"https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fm-radzikowski%2Faws-sdk-client-mock%2Fissues%2Fnew\" target=\"_blank\">issue on GitHub</a>.</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/f05b644abcf44cce8f167666958bffc2_image.png\" alt=\"image.png\" /><br />\n<a href=\"https://twitter.com/radzikowski_m\" target=\"_blank\"> <strong>Maciej Radzikowski</strong></a></p>\n<p>Maciej is a software developer at <a href=\"https://merapar.com/\" target=\"_blank\">Merapar</a> focused on AWS serverless solutions and sharing knowledge. He writes programming and serverless guides on his blog at <a href=\"https://betterdev.blog/\" target=\"_blank\">BetterDev.blog</a> and shorter tips on Twitter <a href=\"https://twitter.com/radzikowski_m\" target=\"_blank\">@radzikowski_m</a></p>\n"}
目录
亚马逊云科技解决方案 基于行业客户应用场景及技术领域的解决方案
联系亚马逊云科技专家
亚马逊云科技解决方案
基于行业客户应用场景及技术领域的解决方案
联系专家
0
目录
关闭
contact-us