{"id":22108534,"url":"https://github.com/gradedjestrisk/refactoring-test","last_synced_at":"2026-05-14T20:02:15.825Z","repository":{"id":38260331,"uuid":"261685699","full_name":"GradedJestRisk/refactoring-test","owner":"GradedJestRisk","description":"Javascript version of https://www.manning.com/books/unit-testing C# refactor kata","archived":false,"fork":false,"pushed_at":"2023-01-11T02:52:44.000Z","size":1465,"stargazers_count":0,"open_issues_count":18,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-31T13:05:21.935Z","etag":null,"topics":["integration-test","javascript","kata","plpgsql","testing","unit-test"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/GradedJestRisk.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}},"created_at":"2020-05-06T07:37:44.000Z","updated_at":"2020-07-02T12:07:08.000Z","dependencies_parsed_at":"2023-02-08T23:40:12.012Z","dependency_job_id":null,"html_url":"https://github.com/GradedJestRisk/refactoring-test","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/GradedJestRisk/refactoring-test","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GradedJestRisk%2Frefactoring-test","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GradedJestRisk%2Frefactoring-test/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GradedJestRisk%2Frefactoring-test/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GradedJestRisk%2Frefactoring-test/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GradedJestRisk","download_url":"https://codeload.github.com/GradedJestRisk/refactoring-test/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GradedJestRisk%2Frefactoring-test/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33041204,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T13:14:54.681Z","status":"online","status_checked_at":"2026-05-14T02:00:06.663Z","response_time":57,"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":["integration-test","javascript","kata","plpgsql","testing","unit-test"],"created_at":"2024-12-01T09:16:13.454Z","updated_at":"2026-05-14T20:02:15.807Z","avatar_url":"https://github.com/GradedJestRisk.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Table of contents\n- [Introduction](#introduction)\n- [Scope](#scope)\n- [Book theses overview](#book-theses-overview)\n  * [Hypothesis](#hypothesis)\n  * [Conclusion](#conclusion)\n  * [Target testing strategy](#target-testing-strategy)\n- [Implementation](#implementation)\n- [Repository building](#repository-building)\n- [Install](#install)\n  * [Steps](#steps)\n  * [Development purpose](#development-purpose)\n- [Compare implementations](#compare-implementations)\n  * [An overview](#an-overview)\n  * [Details](#details)\n\n# Origin \u0026 inspirations\nThis code* has been ported from a copyrighted C# version, included in the [Unit Testing Principles, Practices, and Patterns](https://www.manning.com/books/unit-testing) book, published by Manning. The author, Vladimir Khorikov, has explicitly allowed such use here. Otherwise stated, all information and quotes in this file comes from the book. I'm not linked in any way with the author; I would like to experiment its proposals and share them with you.\n\n*: except PostgreSQL PL/SQL version, that I wrote by myself. Referred to as pg-pl-sql in codebase, it'a procedural (imperative) language, executed by the database. Basically, it allows mixing \"functional\" SQL statements together using basic imperative structures (control flow, variables, array). The aim of using such an unusual programming language is to show cost/benefits of such a compact implementation.\n\nRegarding hexagonal architecture, there is a plenty of folders naming convention (see the update note in this [blog post](https://blog.octo.com/en/hexagonal-architecture-three-principles-and-an-implementation-example/)):\n * domain:  port / adapter\n * user-side / application\n * server-side / infrastructure\n\nCharacterization testing complies with to [Michael Feathers](https://michaelfeathers.silvrback.com/characterization-testing) definition\n\n# Scope\nOnly entreprise applications are in the scope.\n\u003e An entreprise application is an application that aims at automating or assisting an organization's inner\n\u003e processes. It can take many forms, but usually the characteristics of an entreprise software are:\n\u003e - high business logic complexity;\n\u003e - long project lifespan;\n\u003e - moderate amounts of data;\n\u003e - low or moderate performance requirements.\n\nOnly back-office API is in the scope (GUI are off-scope).\n\n# Book's these overview\n\n## Hypothesis\nAutomated testing aims at making change cheaper, by making sure:\n* the new behaviour is what you expected\n* other existing behaviours have not been affected\n\nAutomated testing is implemented by code, which should be payed for several times:\n* implementation time;\n* maintenance time.\n\nTherefore, test can make change costlier.\n\nSeveral ways to design tests:\n* mockist school \n    * mock all dependencies (eg. most collaborators)\n    * this cause test code bloat and test brittleness: such test raises false positive while refactoring \n* classical school \n    * mock only shared dependencies (eg. DB)\n    * this cause less, but still some test brittleness\n* output-based test\n    * mock only unmanaged shared dependencies (eg. external API) \n    * brittleness is minimum\n* output-based test can be achieved\n    * by restricting side effect to dedicated areas in production code\n    * such containment is provided by indirection, implemented by application architecture pattern: hexagonal, functional\n\nOutput-based test look like the best bet.\n\nTransitioning a codebase to output-based testing is a 2 steps refactoring process:\n * refactor production code (thus breaking existing brittle test) to achieve containment\n * refactor test code\n\n## Conclusion\nCoding is a tradeoff between code performance and change cost\n\n| Indirection  | Code change     | Code performance | Test change |\n|--------------|-----------------|------------------|-------------|\n| None         | costlier        | higher           | costlier    |\n| Some         | cheaper         | lower            | cheaper     |\n| Too many     | costlier        | lower            | costlier    |\n\nIndirection refers to can be implemented by design patterns, layered architecture. \n\nCode change (maintenance) cost can be broken down in time:\n* to understand existing code\n* to make appropriate change\n* fix unintended side effects (regression)\n\nTest change (maintenance) cost can be broken down in time:\n* to understand existing test\n* to make appropriate change to support a code feature change\n* to make appropriate change to support a code refactoring change (false positive)\n\n1: Indirection samples are design patterns, layered architecture \n\n## Target testing strategy\nTest types:\n* unit test \n   * scope :\n      * domain only\n      * all paths\n   * isolation :\n      * use real collaborators\n      * no mock\n* integration test:\n    * scope :\n        * happy path, exercice all components in the least possible scenarios\n        * all other paths untested in unit test\n    * isolation:\n        * use real collaborators in\n            * application, domain \n            * infrastructure : managed dependencies only (eg. DB)\n        * mock \n            * infrastructure: unmanaged dependency only (here, external message bus/API)\n            * using handwritten mock  \n\n# Implementation\nCodebase comes in several flavors, to widen understanding:\n* procedural (no OOP)\n   * pg-pl-sql\n   * Javascript\n* object-oriented\n   * hexagonal architecture\n   \n# Repository building\nIt will follow these steps:\n* port procedural C# codebase from the book to a [procedural JS](../master/src/procedural/javascript)\n* write [characterization test](../master/test/characterization)\n* write [pg-pl-sql port](../master/src/procedural/pg-pl-sql), using characterization test \n* port OOP hexagonal C# codebase from the book to [JS OOP hexagonal](../master/src/object-oriented/hexagonal-event/code/)\n* write test for OOP hexagonal \n    * with best practice from book\n        * [unit](../master/src/object-oriented/hexagonal-event/test/unit/domain/User.test.js)\n        * [integration](../master/src/object-oriented/hexagonal-event/test/integration/change-user-email.test.js)\n    * with anti-patterns (eg. unit-testing everything, using mocks)\n* [compare](#compare-implementations) costs and benefits of each solution\n\n# Install\n\n## Pre-requisites\nYou'll need:\n* node and npm (I used [nvm](https://github.com/GradedJestRisk/js-training/wiki/node#using-nvm))\n* a running postgresql instance:\n    * I used [Docker](https://github.com/GradedJestRisk/db-training/wiki/PostgreSQL#container-docker)\n    * to make optional http call, you'll need [http extension](https://github.com/pramsey/pgsql-http))\n\n## Steps \n* get the source:\n  * with git: clone the repo : \u003ccode\u003egit clone git@github.com:GradedJestRisk/refactoring-test.git \u0026\u0026 cd refactoring-test\u003c/code\u003e\n  * without git: \n    *  or download the source code manually, extract and cd \n    ```\n    curl -LJO https://github.com/GradedJestRisk/refactoring-test/archive/master.zip\n    unzip refactoring-test-master.zip\n    cd    refactoring-test-master\n    ```\n* install dependencies \u003ccode\u003enpm install\u003c/code\u003e\n* setup your database connection in [knexfile.js](../master/knexfile.js)\n```\n    connection: {\n      database: '\u003cDATABASE_NAME\u003e',\n      port:     \u003cPORT\u003e,\n      user:     '\u003cUSER',\n      password: '\u003cPASSWORD\u003e' \n    },\n```\n* create DB structure: run `npm run db:create-schema`\n* run the test `npm test`\n\n## Development purpose\n\nTo interactively make API calls on OOP Hexagonal JS:\n* create sample data with `npm run db:insert-data`\n* start API with `npm start`\n* check it's up\n    * make a health check call `curl --request GET 'http://localhost:3000/health_check'`\n    * check the response `{\"name\":\"refactoring-test\",\"version\":\"1.0.0\",\"description\":\"javascript port of https://www.manning.com/books/unit-testing C# refactor kata\"}% `\n    * check the log `127.0.0.1: GET /health_check --\u003e 200`\n* make an actual call \n    * get user: `curl --request GET 'http://localhost:3000/users/0'`\n    * change its email:    \n    `curl --header \"Content-Type: application/json\" \\`\n    `--request POST \\`\n    `--data '{\"id\":\"0\",\"email\":\"foo@bar.com\"}' \\`\n    `localhost:3000/users/0/email`\n\nTo run characterization tests interactively in your IDE on an implementation:\n* alter the following line in [change-user-email.test.js](../master/test/characterization/change-user-email.test.js)\n```\n} else {\n    // used for interactive\n    sutPath = sutPathProceduralJS;\n}\n```\nTo see all SQL queries issued by JS:\n* enable debug mode by uncommenting the following line in [knexfile.js](../master/knexfile.js)\n```\n  // debug: true\n```\n\n# Compare implementations\n\n## An overview\n\nUnits:\n* time: milliseconds\n* size: characters\n\n| Implementation     | Code \u003cbr\u003e size   | Code \u003cbr\u003e execution time | Unit test \u003cbr\u003e size  | Unit test \u003cbr\u003e  execution time | Integration test \u003cbr\u003e size  | Integration test \u003cbr\u003e execution time | Char. test \u003cbr\u003e execution time | End-to-end test \u003cbr\u003e size | End-to-end test \u003cbr\u003e execution time\n|--------------------|---------------|----------|----------|-------|---------|---------|-------|-------|-------|\n| Procedural DB      | 4 250         | 1 (3 from node)     | N/A      | N/A   | N/A     | N/A     | 1 000  |   N/A  |   N/A |     \n| Procedural JS      | 2 750         | 220                 |  N/A     | N/A   | N/A     | N/A     | 2 000  |   N/A  |   N/A |\n| OOP Hexagonal JS   | 5 590         | 220                 | 2 500    | 370   | 4 258   | 570     | 2 000  |  3 496 | 2 101 |\n\n| Helper            | Code     |\n|-------------------|----------|\n| Char test         | 9 000    |\n| Char test helper  | 1 000    |\n\n## Details\nRun `npm run benchmark`\nTo get procedural pl/sql execution time out of node, execute function `SELECT get_execution_time_micro();`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgradedjestrisk%2Frefactoring-test","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgradedjestrisk%2Frefactoring-test","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgradedjestrisk%2Frefactoring-test/lists"}