{"id":17114488,"url":"https://github.com/anthonychu/azure-functions-test-utils","last_synced_at":"2025-04-13T04:01:32.720Z","repository":{"id":39636093,"uuid":"451787901","full_name":"anthonychu/azure-functions-test-utils","owner":"anthonychu","description":null,"archived":false,"fork":false,"pushed_at":"2022-05-30T10:05:13.000Z","size":156,"stargazers_count":13,"open_issues_count":2,"forks_count":3,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-26T21:02:00.356Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anthonychu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-01-25T08:04:25.000Z","updated_at":"2024-08-06T16:51:40.000Z","dependencies_parsed_at":"2022-07-13T10:30:37.543Z","dependency_job_id":null,"html_url":"https://github.com/anthonychu/azure-functions-test-utils","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anthonychu%2Fazure-functions-test-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anthonychu%2Fazure-functions-test-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anthonychu%2Fazure-functions-test-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anthonychu%2Fazure-functions-test-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anthonychu","download_url":"https://codeload.github.com/anthonychu/azure-functions-test-utils/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248661706,"owners_count":21141450,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T17:17:19.452Z","updated_at":"2025-04-13T04:01:32.669Z","avatar_url":"https://github.com/anthonychu.png","language":"TypeScript","funding_links":[],"categories":["others"],"sub_categories":[],"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\", () =\u003e {\n\n    // Testing a simple HTTP function\n    it(\"should prompt for a username\", async () =\u003e {\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 () =\u003e {\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 () =\u003e {\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\", () =\u003e {\n    let funcCli: FuncCli;\n    let queueClient: QueueClient;\n\n    beforeAll(async () =\u003e {\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 () =\u003e {\n        await funcCli.stop();\n    });\n\n    describe(\"HTTP trigger to queue trigger\", () =\u003e {\n\n        beforeEach(async () =\u003e {\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 =\u003e setTimeout(resolve, 1000));\n                } else {\n                    break;\n                }\n            };\n        });\n\n        it(\"no name provided\", async () =\u003e {\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 () =\u003e {\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\", () =\u003e {\n    let funcCli: FuncCli;\n\n    beforeAll(async () =\u003e {\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 () =\u003e {\n        await funcCli.stop();\n    });\n\n    describe(\"Durable Functions orchestration\", () =\u003e {\n\n        it(\"Orchestration starts and finishes\", async () =\u003e {\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 =\u003e 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```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanthonychu%2Fazure-functions-test-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanthonychu%2Fazure-functions-test-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanthonychu%2Fazure-functions-test-utils/lists"}