{"id":17677784,"url":"https://github.com/adamslack/lambda-unit-testing-workshop","last_synced_at":"2025-03-30T17:47:57.333Z","repository":{"id":122723131,"uuid":"310086521","full_name":"AdamSlack/lambda-unit-testing-workshop","owner":"AdamSlack","description":"A Demo Repo","archived":false,"fork":false,"pushed_at":"2020-11-05T14:11:50.000Z","size":135,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-05T20:06:13.607Z","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/AdamSlack.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-04T18:33:17.000Z","updated_at":"2021-04-06T09:55:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"8a67069c-febc-4331-a6cf-db496e17165b","html_url":"https://github.com/AdamSlack/lambda-unit-testing-workshop","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdamSlack%2Flambda-unit-testing-workshop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdamSlack%2Flambda-unit-testing-workshop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdamSlack%2Flambda-unit-testing-workshop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdamSlack%2Flambda-unit-testing-workshop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AdamSlack","download_url":"https://codeload.github.com/AdamSlack/lambda-unit-testing-workshop/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246358251,"owners_count":20764366,"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-24T07:41:50.357Z","updated_at":"2025-03-30T17:47:57.270Z","avatar_url":"https://github.com/AdamSlack.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lambda-unit-testing-workshop\n\nThis repo provides a simple example lambda implemented using typescript, using Middy to provide some basic validation and other utilities.\n\n## What should you do?\n\nYou should aim to get 100% test covereage and 0 mutations!\n\n## How can i do that?\n\nFinish off writing unit tests for the lambda that is only partially tested!\n\n## How can i check my progress?\n\nThere are two things to check:\n- For unit tests and coverage run: `yarn test`\n- For Mutation tests and results run: `yarn test:mutate`\n\n## What is Mutation Testing?\n\nMutation Testing aims to prove that the quality of your code coverage is sufficient. It does this by changing elements of the codebase before running the suite of unit tests.\n\nIf no unit tests fail for a particular change, then that is flagged as a mutant. Mutants indicate an aspect of the code base that was not sufficiently tested.\n\nTypical things that are subject to being mutated includes such things as:\n- Objects and values passed to functions\n- Operators (e.g. `\u003e`, `\u003c=`, etc)\n- String literals\n\n## How can i squash mutants?\n\nBy writing more tests! If you find that `stryker` is complaining about a particular line, then there is likely some tests and assertions missing.\n\n## Setup\n\n1. Run a `yarn install`\n2. run a `yarn test` to validate dependencies are installed and tests can be run\n3. run a `yarn test:mutate` to validate mutation testing can take place\n\nYou might need to install yarn and stryker separately, but you shouldn't do.\n\n- Link to [Stryker Mutator](https://stryker-mutator.io/)\n- Link to [Yarn](https://classic.yarnpkg.com/en/)\n\nYou could just use `npm`, whatever you prefer.\n\n# Principles for testing lambda\n\nThe following is a set of general guidelines that can be applied to your testing of lambda. Like with all things software engineering, a degree of pragmatism is required should you apply these to your own lambda testing approaches.\n\n\n## Test your lambda through the front door.\n\n\u003e Excercise the lambda in the same way as it is going to be used in a deployed environment.\n\nTesting is all about proving that your code works. You need to be able to state with confidence that what you have implemented will behave as expected in a 'production' environment.\n\nA challenge with many forms of testing is having confidence that what works locally \"on your machine\" will still work when deployed into a different environment. Writing tests that mimic reality improve the likelihood that your will work as it should\n\nBy only writing unit tests that 'go through the front door' you are ensuring that all your tests replicate the reality of how your lambda functions are to be used.\n\nWhether it is Via API Gateway, SNS, SQS, or any other integration, your lambda is always invoked in the same way - A Handler is executed with some payload.\n\n## Assert at the boundaries.\n\n\u003e Mocking that which is external to your lambda, make assertions against those boundaries\n\nMocking the interactions that are external to you lambda means that you can validate the behavior of your code under a range of different circumstances. You can then make assertions that your lambda initiates, or responds to these interactions as you expect.\n\nAs small and discrete packages of business logic there is little value in testing the 'internals' of the lambda in isolation. Which is to say, producing excessive mocks and for code that is packaged and deployed with your lambda does not help in proving that your lambda behaves as it should.\n\nExample External interactions to Mock:\n- Making a http request\n- Pushing a message onto SQS\n- Querying DynamoDB\n- Recording a Log\n- Recording a CloudWatch Metric\n\nExample Internal Interactions you want to avoid Mocking:\n- Using an internal helper function\n- Proxy Services for external interactions\n\n## Complex Testing means Complex Lambda\n\n\u003e Complexity in tests is a good indicator of an overly complicated lambda\n\nShould you find that producing a set of small and simple tests is becoming challenging, then you may wish to reconsider the implementation of your lambda. It is an indicator that your lambda is attempting to do too much.\n\nSome Questions to ask yourself:\n- Am i needing to heavily mock the internals to produce easy to understand unit tests?\n- Are there just too many scenarios/behaviours to test?\n- Am i struggling to cleanly mock all the externals to this lambda?\n- Are there huge branches in the implementation?\n\nShould you answer yes to any of those, then you might want to consider refactoring your lambda into separate functions. Each lambda should pick one thing, and do that one thing well!\n\nLooking at other AWS Technologies could assist you with the task of producing simple Lambda Functions:\n- SNS and subscription filtering on message attributes can be used to simplify branching logic. \n- Step Functions can simplify the implementation of workflows\n\n## Treat your Lambda as an isolated unit.\n\n\u003e Treating your lambda as the smallest discrete testable unit shifts testing focus to more meaningful levels\n\nTesting occurs at different levels, with different boundaries. Different teams call each level of testing something different, but generally go from small-in-scope unit tests, up to widely scoped e2e tests.\n\nIn a serverless architecture, the range of tools that are in use, and the complexity of the system can grow very rapidly. As such testing often focuses on your usage of various servless tools and their integration with each other.\n\nBy treating each lambda the smallest testable unit of code, you are able to able to start testing your usage of lambda much quicker, and begin testing the integration of your lambda functions with the rest of your estate.\n\nThis also allows you to test the behaviours of your lambda without coupling tests too tightly to their implementation. Refactoring the implementation is easier as you have tests validating that the end behaviour doesn't change. You can even begin to write the tests that descibe expected behaviours before beginning any implementation!\n\n## One Assertion Per Test\n\n\u003e Testing one thing at a time makes it clearer what has failed\n\nThis isn't strictly a good rule for testing lambda, but best practice for all kinds of testing.\n\nA Test typically follow the following steps:\n1. Setup Scenario\n2. Execute Scenario\n3. Make Assertion\n4. Teardown Scenario\n\nOften tests are found to be making multiple assertions during the execution of a scenario. This can impeded development by making it difficult to see all which assertions passed and which failed, especially if your test runner stops a test after a single failing scenario.\n\nWith many common testing libraries like `jest` utilities are provided to help you repeat the setup and teardown and also make independant assertions of behaviours and interactions","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadamslack%2Flambda-unit-testing-workshop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadamslack%2Flambda-unit-testing-workshop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadamslack%2Flambda-unit-testing-workshop/lists"}