{"id":16584862,"url":"https://github.com/mikaelvesavuori/testable-systems-starter","last_synced_at":"2025-08-02T02:12:10.806Z","repository":{"id":101082338,"uuid":"596025869","full_name":"mikaelvesavuori/testable-systems-starter","owner":"mikaelvesavuori","description":"A sample project to use in testing workshops with the theme of testing and \"more testable\" systems.","archived":false,"fork":false,"pushed_at":"2024-08-21T13:52:46.000Z","size":1120,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-06T05:16:23.118Z","etag":null,"topics":["boilerplate","learning","serverless","starter","template","testing","workshop"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mikaelvesavuori.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2023-02-01T10:03:13.000Z","updated_at":"2024-08-21T13:52:50.000Z","dependencies_parsed_at":"2025-01-16T16:14:26.705Z","dependency_job_id":"a5240e72-cd03-4147-a3c1-70316329227b","html_url":"https://github.com/mikaelvesavuori/testable-systems-starter","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mikaelvesavuori/testable-systems-starter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikaelvesavuori%2Ftestable-systems-starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikaelvesavuori%2Ftestable-systems-starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikaelvesavuori%2Ftestable-systems-starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikaelvesavuori%2Ftestable-systems-starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mikaelvesavuori","download_url":"https://codeload.github.com/mikaelvesavuori/testable-systems-starter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mikaelvesavuori%2Ftestable-systems-starter/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268326739,"owners_count":24232496,"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","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["boilerplate","learning","serverless","starter","template","testing","workshop"],"created_at":"2024-10-11T22:46:00.700Z","updated_at":"2025-08-02T02:12:10.767Z","avatar_url":"https://github.com/mikaelvesavuori.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Testable Systems Starter\n\nA sample project to use in testing workshops with the theme of testing and building \"more testable\" distributed (serverless) systems.\n\nRelevant theoretical materials include:\n\n- [Distributed Systems — Key Concepts \u0026 Patterns](https://engineering.klarna.com/distributed-systems-key-concepts-patterns-d4d5236b9816)\n- [Fallacies of distributed computing](https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing)\n\nOther good, practical material includes:\n\n- [Understand Legacy Code](https://understandlegacycode.com) is great, see for example [A quick way to add tests when code has database or HTTP calls](https://understandlegacycode.com/blog/quick-way-to-add-tests-when-code-does-side-effects/)\n- [You Don't Hate Mocks; You Hate Side-Effects](https://blog.thecodewhisperer.com/permalink/you-dont-hate-mocks-you-hate-side-effects)\n\n_Based on the [minimalist-serverless-starter](https://github.com/mikaelvesavuori/minimalist-serverless-starter) project._\n\n## Configurations\n\nConfigurations for ESLint and Prettier are reasonable starting points. The TypeScript config is very strict to get the most out of TS features. Serverless Framework is optimized ([ARM architecture](https://aws.amazon.com/blogs/aws/aws-lambda-functions-powered-by-aws-graviton2-processor-run-your-functions-on-arm-and-get-up-to-34-better-price-performance/); short log retention; no versioning), [CORS-activated](https://www.serverless.com/blog/cors-api-gateway-survival-guide/), and set to safer-than-default settings.\n\n## Structure\n\nThe application starting point (the handler) is located at `src/handler.ts` and a first demonstrational test is at `tests/unit/demo.test.ts`. The rest of the tests and other \"finished\" materials are in the `__finished__` folder and might need updates to their import paths when you place them in the root again.\n\n## Prerequisites\n\n- Recent [Node.js](https://nodejs.org/en/) (ideally 18+) installed.\n- Amazon Web Services (AWS) account with sufficient permissions so that you can deploy infrastructure. A naive but simple policy would be full rights for CloudWatch, Lambda, API Gateway, and S3.\n- Ideally some experience with [Serverless Framework](https://www.serverless.com) as that's what we will use to deploy the service and infrastructure.\n\n## Installation\n\nClone, fork, or download the repo as you normally would. Run `npm install`.\n\n## Commands\n\n- `npm start`: Run application locally\n- `npm test`: Test the business/application logic with Jest\n- `npm run build`: Package application with Serverless Framework\n- `npm run deploy`: Deploy application to AWS with Serverless Framework\n- `npm run teardown`: Remove stack from AWS\n\n## Running locally\n\nUsing `npm start` you can start using the local endpoint `http://localhost:3000/greet` to call the service.\n\n```bash\ncurl http://localhost:3000/greet\n```\n\nWhich should respond back with:\n\n```bash\n\"Hi there!\"\n```\n\n---\n\n## Workshop\n\nThe workshop is meant to be dynamic and interactive, but the below outlines an overall learning/experience flow for participants.\n\n### Basics\n\n#### New business requirement\n\n\u003e We need a service to greet people.\n\n#### Concepts\n\n- **Scope**\n  - We use the concept generally, for a more extensive take see [What is in your Testing Scope?](https://medium.com/wix-engineering/what-is-in-your-testing-scope-8846714d4358)\n- **Boundary**\n  - See for example [Defining Test Boundaries – An example](https://www.simpleorientedarchitecture.com/defining-test-boundaries/) and [Avoid Test Duplication](https://martinfowler.com/articles/practical-test-pyramid.html#AvoidTestDuplication)\n- **[Contra-variant testing](https://blog.cleancoder.com/uncle-bob/2017/10/03/TestContravariance.html)**\n- **[Determinism](https://martinfowler.com/articles/nonDeterminism.html)**\n\n#### Present and discuss\n\n- \"Contra-variant testing\": The benefits of testing the majority of code on a use-case level rather than per-function level.\n- Confidence can be causated by determinism (in code) - determinism can be achieved by controlling side effects.\n\n#### Steps\n\n1. Look at `tests/unit/demo.test.ts` to familiarize yourself with the structure of a typical unit test.\n2. Look at `src/handler.ts`. How can we test this? How might you think about the _scope_ of a given test - bigger, smaller and their pros/cons?\n3. Implement a unit test on the entire handler. What do you foresee as issues with this solution?\n4. Split out \"business logic\" from the handler. How is testing, reliability and confidence improved by doing this?\n5. Reimplement the unit test on business logic, not on the handler.\n\n---\n\n### Dynamic input\n\n#### New business requirement\n\n\u003e We need support for dynamic input/output, i.e. providing and responding with _**your**_ name.\n\n#### Concepts\n\n- **Mutability**\n  - Read [Tiny Programming Principles: Immutability](https://www.tiny.cloud/blog/mutable-vs-immutable-javascript/) and [Immutable object](https://en.wikipedia.org/wiki/Immutable_object)\n  - See also [To mutate or not – on Entities and Value Objects](https://www.schibsted.pl/blog/immutability-entities-and-value-objects/) and the approach in [Elegant Objects](https://www.elegantobjects.org)\n- **Validation**\n- **Invariant**\n- **\"Always valid\" domain model**\n  - Read [Always-Valid Domain Model](https://vkhorikov.medium.com/always-valid-domain-model-706e5f3d24b0), also covers the above concepts\n\n#### Present and discuss\n\n- The dangers of \"dumb\" [POCOs/POJOs](https://enterprisecraftsmanship.com/posts/dto-vs-value-object-vs-poco/) and mutability of data.\n- Leverage prior logical validation if input remains unchanged/un-mutated.\n\n#### Steps\n\n6. Implement new functionality. How do we support both the new and old behaviors?\n7. Think about validation: At which levels can/should we validate? Once, or across all boundaries? How could we be supported by using API-level schema validation? (See `api/Greeter.validator.json` for an example)\n8. Implement validation functions. Demonstrate both structural/compositional (\"functional\") approach and object-oriented (DDD-inspired: value objects, Data Transfer Object, \"always valid state\") approach.\n9. Implement any additional tests.\n\n---\n\n### Third-party dependency\n\n#### New business requirement\n\n\u003e For \"un-named\" requests, we want to send back a response so it looks something like `Hi there, Luke Skywalker!`.\n\n#### Concepts\n\n- **[Observability](https://www.ibm.com/se-en/topics/observability)**\n\n#### Present and discuss\n\n- Testing vs monitoring and observability.\n- Fallacies of distributed computing - What is it really that we want to test when we conduct integration testing?\n\n#### Steps\n\n_**Make sure to uncomment `setupFilesAfterEnv` in `jest.config.js` to get the mocking capability working.**_\n\n10. How do we test an external service?\n11. Getting test data and storing it co-located to our code and tests.\n12. API response mocking using our test data. What about schema changes?\n13. Handling errors and problem states correctly.\n14. Implement tests.\n\n---\n\n### Handling persistence and other side effects\n\n#### New business requirement\n\n\u003e We need to communicate each request to our service by emitting an event.\n\n#### Concepts\n\n- **Side effects**\n  - Read the high-level [Side effect (computer science)](\u003chttps://en.wikipedia.org/wiki/Side_effect_(computer_science)\u003e) and more practical [Side effects](https://dev.to/ruizb/side-effects-21fc)\n\n#### Present and discuss\n\n- How to test around boundaries of systems, especially when using managed products like message queues and databases.\n\n#### Steps\n\n15. Implement an event emitter (see `__finished__/src/infrastructure/emitter/Emitter.start.ts`). What problems do we get when using this for our testing?\n16. Abstract the implementation into an interface and inject a dummy/mock/local variant for testing.\n17. Making an implementation testable (\"test aware\") up until the point of producing potentially adverse side effects.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmikaelvesavuori%2Ftestable-systems-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmikaelvesavuori%2Ftestable-systems-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmikaelvesavuori%2Ftestable-systems-starter/lists"}