{"id":20425445,"url":"https://github.com/tophat/dont-waste-your-ducking-time","last_synced_at":"2025-05-08T16:32:10.694Z","repository":{"id":37879962,"uuid":"209429406","full_name":"tophat/dont-waste-your-ducking-time","owner":"tophat","description":":rooster: An opinionated guide on how to test Redux ducks","archived":false,"fork":false,"pushed_at":"2023-12-15T05:58:44.000Z","size":5586,"stargazers_count":27,"open_issues_count":19,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-05-15T15:31:06.595Z","etag":null,"topics":["actions","ducks","reducers","redux","redux-saga","selectors","testing"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tophat.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":"2019-09-19T00:40:50.000Z","updated_at":"2024-05-15T15:31:06.595Z","dependencies_parsed_at":"2023-02-02T09:16:16.292Z","dependency_job_id":null,"html_url":"https://github.com/tophat/dont-waste-your-ducking-time","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":"tophat/new-project-kit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tophat%2Fdont-waste-your-ducking-time","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tophat%2Fdont-waste-your-ducking-time/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tophat%2Fdont-waste-your-ducking-time/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tophat%2Fdont-waste-your-ducking-time/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tophat","download_url":"https://codeload.github.com/tophat/dont-waste-your-ducking-time/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224742403,"owners_count":17362232,"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":["actions","ducks","reducers","redux","redux-saga","selectors","testing"],"created_at":"2024-11-15T07:13:22.879Z","updated_at":"2024-11-15T07:13:23.562Z","avatar_url":"https://github.com/tophat.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Don't Waste Your Ducking Time\n\n\u003cspan\u003e\u003cimg align=\"right\" src=\"logo.png\" alt=\"Logo\"\u003e\u003c/span\u003e\n\n[![Builds](https://img.shields.io/circleci/project/github/tophat/dont-waste-your-ducking-time/master.svg)](https://circleci.com/gh/tophat/dont-waste-your-ducking-time)\n[![codecov](https://codecov.io/gh/tophat/dont-waste-your-ducking-time/branch/master/graph/badge.svg)](https://codecov.io/gh/tophat/dont-waste-your-ducking-time)\n[![Dependabot Status](https://api.dependabot.com/badges/status?host=github\u0026repo=tophat/dont-waste-your-ducking-time)](https://dependabot.com)\n[![Slack workspace](https://slackinvite.dev.tophat.com/badge.svg)](https://opensource.tophat.com/slack)\n[![All Contributors](https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square)](#contributors-)\n\n## Overview 📃\n\nThis repo is a simple example project demonstrating how to test the different\ncomponents of a Redux application.\n\nThe general idea: **Don't write individual unit tests for your action creators,\nreducers, and selectors; instead, write tests that test the whole \"duck\"**.\n\nThis strategy comes from [this Redux issue\nthread](https://github.com/reduxjs/redux/issues/1171) in which\n[@bvaughn](https://github.com/bvaughn) describes a way of testing Redux\napplications in order to get \"the most bang for your buck\" (dare I say \"bang\nfor your duck\"?).\n\nSee the [motivation section](#motivation-) for more details on this testing strategy.\n\n## Examples 🐓\n\nExamples of **bad** duck tests are found [here](./src/bad.test.js).\n\nExamples of **good** duck tests are found [here](./src/good.test.js).\n\nExamples of **good** duck tests **involving sagas** are found\n[here](./src/good.saga.test.js).\n\n## Motivation 💪\n\nFirst of all, what is a duck? It's a combination of related action creators, a\nreducer, and selectors. (It can also include other Redux-related paraphernalia,\nsuch as sagas.) Action creators create actions that when dispatched, cause\nstate changes in the reducer, which ultimately are manifested in different\nvalues returned by the selectors.\n\nYou can read more about ducks in the\n[ducks-modular-redux](https://github.com/erikras/ducks-modular-redux) project.\n\nHow do we test our ducks? The redux documentation gives\n[examples](https://redux.js.org/recipes/writing-tests#action-creators) of how\nto write unit tests for action creators, reducers, and selectors, i.e. how to\ntest them each in isolation. So why don't we start by writing unit tests?\nThat's what the [testing\npyramid](https://martinfowler.com/articles/practical-test-pyramid.html)\nsuggests, after all:\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://martinfowler.com/articles/practical-test-pyramid/testPyramid.png\" alt=\"testing pyramid\"\u003e\n\u003c/p\u003e\n\nThere are some examples of these kind of unit tests in this project, in [one of\nthe duck test files](./src/bad.test.js).\n\nLet's step back for a moment and recall the purpose of tests:\n\n- Tests give us confidence that our code is working as intended\n- Tests give us confidence that our code is working as intended _after we do refactoring_\n\nSo good tests have the following properties:\n\n- They fail when the code doesn't work anymore\n- They don't fail when the code is still working\n- They test code as it actually is used in production\n\nWith that in mind, consider what happens to our isolated action creator tests,\nreducer test, and selector tests when we do the following:\n\n- Refactor our state shape\n- Accidentally introduce a bug\n\nIt turns out lots of bad things happen:\n\n- The reducer tests fail upon refactoring, even though our code is still working, because they test implementation details. We have to mock the state.\n- The selector tests disconcertingly DON'T fail when we introduce a bug in the state, because we're mocking the state. We don't have confidence that the entire action -\u003e reducer -\u003e selector contract is fulfilled.\n- Not all of the tests pass _even when the code is actually working_.\n\nHow do we fix these problems?\n\n_Write tests that test the whole duck!_ You can see examples of such tests in\n[another duck test file](./src/good.test.js). Notice how these tests are:\n\n- Resilient to state refactoring\n- Test the _real_ state shape using a _real_ store, just like what happens in production\n- Still fast\n- Give confidence that all of our action -\u003e reducer -\u003e selector contracts are fulfilled, because it actually tests the contract\n\nThink of a duck as having a public API: action creators and selectors are how\nyou interact with the duck. They represent a \"public interface\" to the duck. If\nyou only write tests using action creators and selectors, your tests are\nresilient to change as long as you maintain the same interface for your action\ncreators and selectors.\n\nYou're not going to have an action if it doesn't trigger a state change, or at\nleast have the potential to trigger some state change. You're not going to have\na selector unless it gives you part of the state that gets changed by the\ndispatch of some action. So when it comes to ducks, don't think of _isolated_\nunit tests, think of the combined action/reducer/selector tests. The _unit_ you\nwant to be testing is the \"three-part unit\" of action creator, reducer, and\nselector. They are not distinct entities, they only have meaning when\nconsidered in relation to each other.\n\n## Installation 🔨\n\nSet up:\n\n```\nyarn install\nyarn start\n```\n\nRun tests:\n\n```\nyarn test\n```\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://msrose.github.io\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/3495264?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMichael Rose\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/tophat/dont-waste-your-ducking-time/commits?author=msrose\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/tophat/dont-waste-your-ducking-time/commits?author=msrose\" title=\"Tests\"\u003e⚠️\u003c/a\u003e \u003ca href=\"https://github.com/tophat/dont-waste-your-ducking-time/commits?author=msrose\" title=\"Documentation\"\u003e📖\u003c/a\u003e \u003ca href=\"#infra-msrose\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://christianbattista.com\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/241211?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eChristian Battista\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/tophat/dont-waste-your-ducking-time/commits?author=cbattista\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://noahnu.com\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/1297096?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eNoah\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/tophat/dont-waste-your-ducking-time/commits?author=noahnu\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-enable --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n\n## Credits\n\nThanks to [LogoMakr](https://logomakr.com/) for having an impressive bounty of free stock duck images.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftophat%2Fdont-waste-your-ducking-time","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftophat%2Fdont-waste-your-ducking-time","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftophat%2Fdont-waste-your-ducking-time/lists"}