{"_id":"@anthonychu/azure-functions-test-utils","_rev":"1-a684f706f78410fd61d19ea758fee770","name":"@anthonychu/azure-functions-test-utils","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@anthonychu/azure-functions-test-utils","version":"0.1.0","description":"Utilities for testing Azure Functions","main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1","build":"cp ../README.md . && cp ../LICENSE . && tsc"},"dependencies":{"@azure/functions":"^1.2.3","@types/cross-spawn":"^6.0.2","@types/node":"^17.0.12","@types/wait-on":"^5.3.1","cross-spawn":"^7.0.3","@types/node-fetch":"^2.5.12","node-fetch":"^2.6.7","tree-kill":"^1.2.2","typescript":"^4.0.0","wait-on":"^6.0.0"},"keywords":[],"author":{"name":"Anthony Chu","email":"antchu@microsoft.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/anthonychu/azure-functions-test-utils.git"},"bugs":{"url":"https://github.com/anthonychu/azure-functions-test-utils/issues"},"homepage":"https://github.com/anthonychu/azure-functions-test-utils#readme","_id":"@anthonychu/azure-functions-test-utils@0.1.0","_nodeVersion":"14.15.4","_npmVersion":"6.14.12","dist":{"integrity":"sha512-2n8QTNu5hiqCiUSMDtcHKugyRbTqYfePaju13jC/P4uqd35GEnhk6G9B/uAXqIioBSZXbpXqzh35uip/0Nsg/Q==","shasum":"66d96090061c01d2c2c9ec2f9cfaf222f2b36e89","tarball":"https://registry.npmjs.org/@anthonychu/azure-functions-test-utils/-/azure-functions-test-utils-0.1.0.tgz","fileCount":7,"unpackedSize":30946,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh95f0CRA9TVsSAnZWagAArqUP/3bs6HkSAnxW893Mgt+l\nXqNBZlGOsOZOhvcqnX8KY02vi9qugyK0W/xNbPegtKxn5Zi0eo12VtplDDjR\nlT6ALyeQamQPoL95tppnYEYBgpunp4w9Z/oeFLOlHb5JbayyFacNFsPhFobB\npM1buxHq427hKyJaSlxc44XzaFtWH8BU+VDnda4wRWQtLVpIj763niZ9WKb2\nGwntb9tvfGYSSyqffGzAxuFT8MEwxOHgvXhuOGx+vuApJsodyyiRH7z64t40\nBIoSnmEtlF8goDdY9fVk7CMIo0gm4kHJ/npFllZjaCFeBj63ATHJpJiJjO/2\nb8jB4pX3PZngm45pYREm2MZ7g3x0jUY4kjRAb3zAMSArlJXg9uaqyD/lpnn2\neqTsJMVNgyMIDdAVQ/evIQMfJ58gaoAJuDRAHMJ7N1qCebaGYGMTZG3qiu2s\nvX3+tRt/lzWdeeXDXVAwP0RQLuuO7yIyIr4LFRTbcJLMjKb0Oat1yvaRb95i\niYNYeLEPfqtsmE7dLAQTpP6zyMdigCjCyoDcHeYgD6bFiI4Os+cnzgcX+xTF\njDxfrZuvOzZABpDhZ/MfIqXXjGiykSvFHm+jPaL48zqD4ZDlm//TmGA4rDX2\nZLke1hCOg1sd6jOiCdAyqYZ3aM14MpD9IInO+FqsKixgm4q/FID7l/84oCHQ\nwdAK\r\n=gpTv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHxMzR4hOJ3915GQ7pp2QDFxtCxHPFKSN1V7Zp9tqxFJAiEAx9qtbRAItGYbY7VW3U7wPY8PjX38XCfhKKYxMORxWFk="}]},"_npmUser":{"name":"anthonychu","email":"anthony@anthonychu.ca"},"directories":{},"maintainers":[{"name":"anthonychu","email":"anthony@anthonychu.ca"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/azure-functions-test-utils_0.1.0_1643616244564_0.18685128582825206"},"_hasShrinkwrap":false}},"time":{"created":"2022-01-31T08:04:04.490Z","0.1.0":"2022-01-31T08:04:04.700Z","modified":"2022-04-04T14:21:41.778Z"},"maintainers":[{"name":"anthonychu","email":"anthony@anthonychu.ca"}],"description":"Utilities for testing Azure Functions","homepage":"https://github.com/anthonychu/azure-functions-test-utils#readme","keywords":[],"repository":{"type":"git","url":"git+https://github.com/anthonychu/azure-functions-test-utils.git"},"author":{"name":"Anthony Chu","email":"antchu@microsoft.com"},"bugs":{"url":"https://github.com/anthonychu/azure-functions-test-utils/issues"},"license":"MIT","readme":"# Azure Functions Test Utilities\n\nA library for testing Azure Functions.\n\n## Installation\n\n    $ npm install -D @anthonychu/azure-functions-test-utils\n\n## Usage\n\n### Unit tests\n\nYou can unit test Azure Functions just like any other JavaScript code. Each function is exported from a Node.js module. Import it into a test and run it.\n\nTo help with mocking the Azure Functions context and HTTP requests/responses, this library includes some classes:\n\n* `TestContext`: A mock context for Azure Functions.\n* `TestHttpRequest`: A mock HTTP request.\n* `TestHttpResponse`: A mock HTTP response.\n\n#### Example\n\n```typescript\nimport func from \"../../HttpTrigger1/index\"\nimport { TestContext } from \"@anthonychu/azure-functions-test-utils\";\n\ndescribe(\"HttpTrigger1 unit tests\", () => {\n\n    // Testing a simple HTTP function\n    it(\"should prompt for a username\", async () => {\n        const context = new TestContext();\n\n        await func(context, context.req);\n\n        expect(context.res.body).toEqual(\"This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.\");\n    });\n\n    // Testing context.log()\n    it(\"should call context.log\", async () => {\n        const context = new TestContext();\n        const spy = jest.spyOn(context, \"log\");\n\n        await func(context, context.req);\n        \n        expect(spy).toHaveBeenCalledTimes(1);\n    });\n\n    // Testing output binding\n    it(\"should output the username in body to queue\", async () => {\n        const context = new TestContext();\n        context.req.body = { name: \"test\" };\n\n        await func(context, context.req);\n\n        expect(context.bindings.outputQueueItem).toEqual({ name: \"test\" });\n    });\n});\n```\n\n### End-to-end tests\n\nYou can run end-to-end tests against Azure Functions running locally using Azure Functions Core Tools. This library includes a `FuncCli` class to help you manage Azure Functions Core Tools.\n\nSome members available on `FuncCli` are:\n\n* Methods\n    - `start(opts)`: Start Azure Functions Core Tools.\n    - `stop()`: Stop Azure Functions Core Tools.\n    - `waitForInvocation(functionName)`: Wait for an invocation of a function and return its status.\n    - `waitForInvocations(functionName, times)`: Wait for a function to be invoked the specified number of times and return their statuses.\n    - `fetch(url, opts)`: Make an HTTP request to the function app. Same as `node-fetch`, but you can pass a path as the URL and the Azure Functions Core Tools base URL will be automatically prepended.\n* Properties\n    - `baseUrl`: The base URL of the Azure Functions Core Tools.\n\n#### Prerequisites\n\n* Install Azure Functions Core Tools.\n\n    `npm install -g azure-functions-core-tools@4`\n\n    - Ensure you install the version required by your app.\n    - Ensure you install it globally.\n\n* If your app requires a Storage account or other resources, ensure you have created them in Azure or you have a local emulator running.\n\n#### Advanced example\n\nThe app consists of two functions:\n\n- `HttpTrigger1`: An HTTP function that returns a greeting and also sends a message to a queue.\n- `QueueTrigger1`: A queue triggered function that is triggered when a message appears in the queue.\n\n```typescript\nimport { FuncCli } from \"@anthonychu/azure-functions-test-utils\";\nimport { QueueClient } from \"@azure/storage-queue\";\n\njest.setTimeout(30000);\n\ndescribe(\"End-to-end tests\", () => {\n    let funcCli: FuncCli;\n    let queueClient: QueueClient;\n\n    beforeAll(async () => {\n        const storageConnectionString = \"UseDevelopmentStorage=true\";\n\n        // create the queue if it doesn't exist\n        queueClient = new QueueClient(storageConnectionString, \"test-queue\");\n        await queueClient.createIfNotExists();\n\n        const funcEnv = {\n            \"AzureWebJobsStorage\": storageConnectionString,\n            \"FUNCTIONS_WORKER_RUNTIME\": \"node\",\n        };\n\n        // start Azure Functions Core Tools\n        funcCli = new FuncCli();\n        await funcCli.start({ port: 7071, cwd: process.cwd(), env: funcEnv });\n    });\n\n    afterAll(async () => {\n        await funcCli.stop();\n    });\n\n    describe(\"HTTP trigger to queue trigger\", () => {\n\n        beforeEach(async () => {\n            // clear messages from queue and wait for operation to complete\n            await queueClient.clearMessages();\n            let messageCount: number = 0;\n            while (true) {\n                const props = await queueClient.getProperties();\n                messageCount = props.approximateMessagesCount ?? 0;\n\n                if (messageCount) {\n                    await new Promise(resolve => setTimeout(resolve, 1000));\n                } else {\n                    break;\n                }\n            };\n        });\n\n        it(\"no name provided\", async () => {\n            const result = await funcCli.fetch(\"/api/HttpTrigger1\");\n            // verify HTTP response\n            expect(await result.text()).toEqual(\"This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.\");\n        });\n\n        it(\"name in query string\", async () => {\n            // declare that we want to wait for one invocation of the queue function\n            const queueTrigger1Invocation = funcCli.waitForInvocation(\"QueueTrigger1\");\n\n            // make an HTTP request to function that outputs a queue message\n            const result = await funcCli.fetch(\"/api/HttpTrigger1?name=test\");\n            expect(await result.text()).toEqual(\"Hello, test. This HTTP triggered function executed successfully.\");\n\n            // wait for queue trigger to be invoked once\n            const queueTrigger1Result = await queueTrigger1Invocation;\n            expect(queueTrigger1Result.status).toEqual(\"Succeeded\");\n        });\n\n    });\n});\n```\n\n#### Durable Functions example\n\n```typescript\nimport { FuncCli } from \"@anthonychu/azure-functions-test-utils\";\n\n// extend the Jest timeout to allow for the Azure Functions Core Tools to start\njest.setTimeout(30000);\n\ndescribe(\"End-to-end tests\", () => {\n    let funcCli: FuncCli;\n\n    beforeAll(async () => {\n        // configure environment variables needed to run the app\n        const storageConnectionString = \"UseDevelopmentStorage=true\";\n        const funcEnv = {\n            \"AzureWebJobsStorage\": storageConnectionString,\n            \"FUNCTIONS_WORKER_RUNTIME\": \"node\",\n        };\n\n        // start Azure Functions Core Tools\n        funcCli = new FuncCli();\n        await funcCli.start({ port: 7071, cwd: process.cwd(), env: funcEnv });\n    });\n\n    afterAll(async () => {\n        await funcCli.stop();\n    });\n\n    describe(\"Durable Functions orchestration\", () => {\n\n        it(\"Orchestration starts and finishes\", async () => {\n\n            // call the Durable Functions HTTP starter to start an orchestration\n            const httpStartResponse = await funcCli.fetch(\"/api/HelloHttpStart\");\n            const { id: instanceId, statusQueryGetUri } = await httpStartResponse.json();\n\n            // wait for the orchestration to no longer be running\n            let result: any; \n            while (true) {\n                const statusResponse = await funcCli.fetch(statusQueryGetUri);\n                result = await statusResponse.json();\n\n                if (result.runtimeStatus !== \"Completed\") {\n                    await new Promise(resolve => setTimeout(resolve, 1000));\n                } else {\n                    break;\n                }\n            }\n\n            // verify the orchestration completed successfully and instance id matches\n            expect(result.runtimeStatus).toEqual(\"Completed\");\n            expect(result.instanceId).toEqual(instanceId);\n        });\n    });\n});\n```","readmeFilename":"README.md"}