{"id":25092820,"url":"https://github.com/biril/jasq","last_synced_at":"2025-04-16T01:54:04.102Z","repository":{"id":11778607,"uuid":"14317703","full_name":"biril/jasq","owner":"biril","description":"AMD dependency injector integrated with Jasmine","archived":false,"fork":false,"pushed_at":"2015-03-07T15:19:04.000Z","size":811,"stargazers_count":6,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-04-07T14:46:57.945Z","etag":null,"topics":["dependency-injection","jasmine","mocking","requirejs","stubbing","test"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/biril.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-11-12T00:21:55.000Z","updated_at":"2020-12-13T20:24:22.000Z","dependencies_parsed_at":"2022-08-31T04:21:56.696Z","dependency_job_id":null,"html_url":"https://github.com/biril/jasq","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biril%2Fjasq","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biril%2Fjasq/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biril%2Fjasq/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/biril%2Fjasq/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/biril","download_url":"https://codeload.github.com/biril/jasq/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249183099,"owners_count":21226140,"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":["dependency-injection","jasmine","mocking","requirejs","stubbing","test"],"created_at":"2025-02-07T14:34:58.654Z","updated_at":"2025-04-16T01:54:04.085Z","avatar_url":"https://github.com/biril.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"Jasq\n====\n\n[![Build Status](https://travis-ci.org/biril/jasq.png)](https://travis-ci.org/biril/jasq)\n[![Bower version](https://badge.fury.io/bo/jasq.png)](http://badge.fury.io/bo/jasq)\n\n[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD) dependency injector integrated with\n[Jasmine](https://github.com/pivotal/jasmine).\n\nJasq simplifies testing AMD modules by overloading Jasmine's `describe` and `it` to\n\n* maintain spec atomicity, avoiding persistent module state\n* allow mocking of the tested module's dependencies, per suite or per spec\n\nJasq is built on the assumption that any Jasmine suite will concern (and thus, test / define the\nspecs for) nothing more than a single module. The Jasq version of `describe` allows specifying the\ntested module (by name) and ensures that it is made available to all contained specs, defined with\n`it`. These gain access to the tested module (through a `module` argument) and may easily provide\nad-hock mocks for any and all of its dependencies. The tested module is reloaded per spec and uses\nany mocked dependencies defined. Mocks may also be defined at the suite level, to be reused for all\ncontained specs.\n\nTo implement dependency injection, Jasq wraps Jasmine's `describe` \u0026amp; `it` global functions and\nadditionally provides overloaded versions which differ in the parameters they accept. These act as\nextentions to Jasmine's built in functionality to be used as (and if) needed:\n\n```javascript\n// Invoke 'describe' - do not associate a module which the suite\ndescribe(\"My suite\", function () {\n  // .. Jasmine specs ..\n});\n\n// Invoke 'describe' passing a moduleName argument to associate the suite\n//  with the module. Contained specs gain access to the module\ndescribe(\"My suite\", \"tested/module/name\", function () {\n  // .. specs ..\n});\n\n// Invoke 'describe' passing a suiteConfig hash as a second argument. Allows\n//  specifying mocks through suiteConfig.mock\ndescribe(\"My suite\", {\n  moduleName: \"tested/module/name\",\n  mock: function () {\n    // Return a hash of mocked dependencies\n  },\n  specify: function () {\n    // .. specs ..\n  }\n});\n\n// Invoke 'it'\nit(\"should do something\", function () {\n  // .. expectations ..\n});\n\n// Invoke 'it' expecting a module. Specs defined within a suite associated\n//  with a module will receive one. Additionally they will receive the\n//  module's dependecies\nit(\"should do something\", function (module, dependencies) {\n  // .. expectations ..\n});\n\n// Invoke 'it' passing a specConfig hash as a second argument. Allows\n//  specifying mocks through specConfig.mock\nit(\"should do something\", {\n  mock: {\n    // Define mocked dependencies\n  },\n  expect: function (module, dependencies) {\n    // .. expectations ..\n  })\n};\n```\n\nJasq uses [RequireJS](https://github.com/jrburke/requirejs) for loading modules and is compatible\nwith [Jasmine, versions \u003e= 2.0.0](https://github.com/pivotal/jasmine/releases). The current\nrevision is tested against Jasmine v2.0.2 and only in a browser environment - support for Node is\nwork in progress.\n\nThe following examples, while not a complete reference, should cover all essential use cases for\nJasq. Further insight may be gained by taking a look\n[the included example](https://github.com/biril/jasq/tree/master/example),\n[the project's test suite](https://github.com/biril/jasq/tree/master/test) and - of course -\n[the source](https://github.com/biril/jasq/blob/master/jasq.js). For the latter, an\n[annotated version](http://biril.github.io/jasq/) is also maintained. Also, please consider\n[not using it](#now-dont-use-it).\n\n\nJasq by example\n---------------\n\nConsider modules `modA`, `modB` where the latter is a dependency of the former:\n\n```javascript\n// Defined in ModB.js:\ndefine(function () {\n  return {\n    getValue: function () {\n      return \"B\";\n    }\n  };\n});\n\n// Defined in modA.js:\ndefine([\"modB\"], function (modB) {\n  return {\n    getValue: function () {\n      return \"A\";\n    },\n    getValueAfterAWhile (cb) {\n      setTimeout(function () {\n        cb(\"A\");\n      }, 100)\n    },\n    getModBValue: function () {\n      return modB.getValue();\n    }\n  };\n});\n```\n\nA test suite for `modA` should be defined as a module hosting the relevant specs. It should require\nJasq (but not the tested `modA` itself):\n\n```javascript\ndefine([\"jasq\"], function () {\n  // Implement modA test suite\n});\n```\n\nDefine the test suite by invoking `describe`, passing the module name as an additional parameter:\n\n```javascript\nrequire([\"jasq\"], function () {\n  // The name of the tested module - 'modA' - is passed as a 2nd parameter\n  //  to the describe call\n  describe(\"The modA module\", \"modA\", function () {\n    // Implement modA specs\n  });\n});\n```\nThis will make the module available to all specs within the suite as the expectation-function\npassed to any nested `it` will now be invoked with `modA` as an argument:\n\n```javascript\nrequire([\"jasq\"], function () {\n  describe(\"The modA module\", \"modA\", function () {\n\n    // The module is passed to specs within the suite, as a parameter\n    it(\"should have a value of 'A'\", function (modA) {\n      expect(modA.getValue()).toBe(\"A\"); // Passes\n    });\n  });\n});\n```\n\nNote that the module will also be available to specs within _nested_ suites:\n\n```javascript\nrequire([\"jasq\"], function () {\n  describe(\"The modA module\", \"modA\", function () {\n\n    describe(\"its value\", function () {\n\n      // The module is also passed to specs within the nested suite\n      it(\"should be 'A'\", function (modA) {\n        expect(modA.getValue()).toBe(\"A\"); // Passes\n      });\n    });\n  });\n});\n```\n\nAdditionally, Jasq ensures that module state will not be persisted across specs:\n\n```javascript\nrequire([\"jasq\"], function () {\n  describe(\"The modA module\", \"modA\", function () {\n\n    // This spec modifies modA\n    it(\"should have a value of 'C' when tweaked\", function (modA) {\n      modA.getValue = function () {\n        return \"C\";\n      };\n      expect(modA.getValue()).toBe(\"C\"); // Passes\n    });\n\n    // This spec is passed the original, unmodified modA\n    it(\"should have a value of A\", function (modA) {\n      expect(modA.getValue()).toBe(\"A\"); // Passes\n    });\n  });\n});\n```\n\nTo mock `modA`'s dependencies, invoke `it` passing a `specConfig` hash as a second argument. Use\nthe `specConfig.mock` property to define a mapping of dependencies (module names) to mocks, as you\nsee fit. Pass the expectations function through the `specConfig.expect` property. In the following\nexample, `modB` is mapped to a `mockB` object:\n\n```javascript\nrequire([\"jasq\"], function () {\n  describe(\"The modA module\", \"modA\", function () {\n\n    // Define a mock for modB\n    var mockB = {\n      getValue: function () {\n        return \"C\";\n      }\n    };\n\n    // modA will use the mocked version of modB\n    it(\"should expose modB's value\", {\n      mock: {\n        modB: mockB\n      },\n      expect: function (modA) {\n        expect(modA.getModBValue()).toBe(\"C\"); // Passes\n      }\n    });\n  });\n});\n```\n\nSpecs additionally receive a `dependencies` argument which may be used to directly access any\nmocked dependencies:\n\n```javascript\nrequire([\"jasq\"], function () {\n  describe(\"The modA module\", \"modA\", function () {\n\n    // Mocked modB may be accessed through 'dependencies.modB'\n    it(\"should expose modB's value\", {\n      mock: {\n        modB: {} // Mocking with an empty object\n      },\n      expect: function (modA, dependencies) {\n        dependencies.modB.getValue = function () {\n          return \"D\";\n        };\n        expect(modA.getModBValue()).toBe(\"D\"); // Passes\n      }\n    });\n  });\n});\n```\n\nOften, it may be useful to access a dependency without necessarily creating a mock beforehand. The\n`dependencies` hash may be used to access any dependency, mocked or not:\n\n```javascript\nrequire([\"jasq\"], function () {\n  describe(\"The modA module\", \"modA\", function () {\n\n    // modB may be accessed through 'dependencies.modB'\n    it(\"should delegate to modB to expose modB's value\", function (modA, dependencies) {\n      spyOn(dependencies.modB, \"getValue\");\n      modA.getModBValue();\n      expect(dependencies.modB.getValue).toHaveBeenCalled(); // Passes\n    });\n  });\n});\n```\n\nIn cases where multiple specs make use of the very same mocks, you can avoid repeating their\ndefinitions _per spec_ by providing a 'mocking function' at the suite level. The mocking function\nshould instantiate all needed mocks and return a hash that maps them to dependencies (module\nnames). This will make the mocks available to all specs defined within the suite. Note that the\nmocking function will be invoked - and the mocks will be re-instatiated - _per spec_.\n\nTo do this, `describe` should be invoked with a `suiteConfig` hash as a second argument. Use the\n`suiteConfig.mock` property to pass the mocking function. Assign the name of the tested module to\n`suiteConfig.moduleName` and the specs function to `suiteConfig.specify`:\n\n```javascript\nrequire([\"jasq\"], function () {\n  describe(\"The modA module\", {\n    moduleName: \"modA\",\n    mock: function () {\n\n      // Define a mock for modB\n      return {\n        modB: {\n          getValue: function () {\n            return \"C\";\n          }\n        }\n      };\n    },\n    specify: function () {\n\n      // modA will use the mocked version of modB\n      it(\"should expose modB's value\", function (modA) {\n        expect(modA.getModBValue()).toBe(\"C\"); // Passes\n      });\n\n      // This spec modifies the mocked modB\n      it(\"should not cache modB's value\", function (modA, dependencies) {\n        dependencies.modB.getValue = function () {\n          return \"D\";\n        };\n        expect(modA.getModBValue()).toBe(\"D\"); // Passes\n      });\n\n      // modA will use the mocked version of modB, unmodified\n      it(\"should expose modB's value - again\", function (modA) {\n        expect(modA.getModBValue()).toBe(\"C\"); // Passes\n      });\n    }\n  });\n});\n```\n\nNote that mocks defined at the suite level will be overriden by those defined in specs:\n\n```javascript\nrequire([\"jasq\"], function () {\n  describe(\"The modA module\", {\n    moduleName: \"modA\",\n    mock: function () {\n\n      // Define a mock for modB\n      return {\n        modB: {\n          getValue: function () {\n            return \"C\";\n          }\n        }\n      };\n    },\n    specify: function () {\n\n      // Redefine the modB mock - modA will use the redefined version\n      it(\"should expose modB's value\", {\n        mock: {\n          modB: {\n            getValue: function () {\n              return \"D\";\n            }\n          }\n        },\n        expect: function (modA) {\n          expect(modA.getModBValue()).toBe(\"D\"); // Passes\n        }\n      });\n    }\n  });\n});\n```\n\nAsynchronous specs which are associated with a module, or are part of a suite associated with a\nmodule, can access Jasmine's `done` function as the _third_ argument. For specs which aren't,\n`done` can be accessed as the first (and only) argument:\n\n```javascript\nrequire([\"jasq\"], function () {\n\n  // If spec is associated with a module access 'done' as the third argument\n  describe(\"The modA module\", \"modA\", function () {\n\n    it(\"should have a value of A, after a while\", function (modA, dependencies, done) {\n      modA.getValueAfterAWhile(function (value) {\n        expect(value).toBe(\"A\"); // Passes\n        done(); // Invoked to start the spec\n      });\n    });\n  });\n\n  // Otherwise access 'done' as the first (and only) argument\n  describe(\"Something\", function () {\n\n    it(\"should happen after a while\", function (done) {\n      setTimeout(function () {\n        done(); // Invoked to start the spec\n      }, 100);\n    });\n  });\n});\n```\n\n\nSet up\n------\n\n`bower install jasq` to obtain the latest Jasq plus dependencies. If you prefer to avoid bower,\njust include [jasq.js](https://raw.github.com/biril/jasq/master/jasq.js) in your project along with\n[RequireJS](https://github.com/jrburke/requirejs).\n\nFor a typical example of test-runner configuration please take a look a the included\n[example test suite](https://github.com/biril/jasq/tree/master/example).\n\n\nNow, don't use it\n-----------------\n\nAs an aside, it's worth noting that suites making use of Jasq _will_ incur some added overhead.\nReloading the relevant module(s) per spec relies on appending `\u003cscript\u003e` tags into the document as\nthe suite executes. Additionally it introduces an extra layer of asynchronicity as every spec\nessentially becomes async. There's a also a price to pay in terms of congitive load as Jasq suites\nare arguably less readable than their plain 'ol Jasmine counterparts.\n\nHowever, regardless of specifics concerning performance, it's worth noting that **Ideally** a\nproject should not rely on any form of injection _at the module loader level_ - even if only for\nthe purposes of testing. The opposite may often be a result of less than optimal design choices\npresenting as testing impediments. In particular, the value in Jasq's spec-atomicity and\ndependency-mocking features is often rooted in such patterns as (ab)use of singletons and tight\ncoupling between components.\n\nIndeed, the impracticality of reverting singleton instances to some known state _after their\ninitial instantiation_, leads to the need for their containing modules to be re-loaded _per test_ -\nif those tests are to be atomic. The same can be said for any module which has side effects -\nintroduces state - at load-time. Contrary to this, non-singleton components that don't 'leak\nstate' may simply be loaded once and instantiated anew, per test.\n\nSimilarly, a requirement for dependency injection at the module loader level may be indicative of\ntightly coupled components within the application. A DI pattern at the application level will\nmitigate tight coupling and allow for tests where mocks can be injected in a straightforward manner\n(e.g. as constructor parameters) without the loader's involvement.\n\nHaving said that, there's valid use-cases for Jasq. For example tight coupling may be unavoidable\nwithin design constraints that lie outside the author's control. Such as those introduced by the\nuse of a specific framework. Authors may choose to leverage Jasq on a per-unit basis, rather than\nacross the entirety of the project's test suite, minimizing the relevant overhead to where\nnecessary.\n\n\nTesting / Contributing\n----------------------\n\nThe QUnit test suite may be run in a browser (test/test.html) or on the command line, by\n`npm test`. Note that the latter requires an installation of [phantomjs](http://phantomjs.org).\n\nContributions are obviously appreciated. Please commit your changes on the `dev` branch - not\n`master`. `dev` is always ahead, contains the latest state of the project and is periodically\nmerged back to `master` with the appropriate version bump. In lieu of a formal styleguide, take\ncare to maintain the existing coding style. Please make sure your changes test out green prior to\npull requests.\n\n\nLicense\n-------\n\nLicensed and freely distributed under the MIT License (LICENSE.txt).\n\nCopyright (c) 2013-2014 Alex Lambiris\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbiril%2Fjasq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbiril%2Fjasq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbiril%2Fjasq/lists"}