{"id":13452218,"url":"https://github.com/kensho/ng-describe","last_synced_at":"2025-03-23T19:33:56.692Z","repository":{"id":21910426,"uuid":"25234505","full_name":"kensho/ng-describe","owner":"kensho","description":"Convenient BDD specs for Angular","archived":true,"fork":false,"pushed_at":"2020-08-13T02:55:36.000Z","size":559,"stargazers_count":427,"open_issues_count":19,"forks_count":26,"subscribers_count":60,"default_branch":"master","last_synced_at":"2024-10-04T19:01:55.082Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":false,"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/kensho.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":"2014-10-15T01:54:19.000Z","updated_at":"2023-09-11T03:27:15.000Z","dependencies_parsed_at":"2022-08-17T23:35:31.714Z","dependency_job_id":null,"html_url":"https://github.com/kensho/ng-describe","commit_stats":null,"previous_names":[],"tags_count":62,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kensho%2Fng-describe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kensho%2Fng-describe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kensho%2Fng-describe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kensho%2Fng-describe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kensho","download_url":"https://codeload.github.com/kensho/ng-describe/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221900757,"owners_count":16898987,"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-07-31T07:01:17.216Z","updated_at":"2024-10-28T18:30:46.608Z","avatar_url":"https://github.com/kensho.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# ng-describe [DEPRECATED]\n\n\u003eThis package has been deprecated as it is no longer used by Kensho internally and we do not plan to continue maintaining it. Feel free to fork it or continue to use it as-is!\n\nConvenient BDD specs for Angular.\n\n[![NPM][ng-describe-icon] ][ng-describe-url]\n[![Quality][quality-badge] ][quality-url]\n\n[![Build status][ng-describe-ci-image] ][ng-describe-ci-url]\n[![manpm](https://img.shields.io/badge/manpm-%E2%9C%93-3399ff.svg)](https://github.com/bahmutov/manpm)\n[![Codacy Badge][ng-describe-codacy-image] ][ng-describe-codacy-url]\n[![semantic-release][semantic-image] ][semantic-url]\n\n[![Coverage Status][ng-describe-coverage-image] ][ng-describe-coverage-url]\n[![dependencies][ng-describe-dependencies-image] ][ng-describe-dependencies-url]\n[![devdependencies][ng-describe-devdependencies-image] ][ng-describe-devdependencies-url]\n\nTested against angular v1.2, v1.3 and v1.4,\ndependent projects tested using [dont-break][dont-break] - [![Circle CI] [circle-icon] ][circle-url].\n\nRead [Unit testing AngularJS using ng-describe](http://glebbahmutov.com/blog/1-2-3-tested/) tutorial,\nlook through [Unit testing](http://slides.com/bahmutov/ng-describe) slides.\n\nJoin [Kensho](https://kensho.com/#/careers) and change the way financial industry analyzes information.\nWe love open source and use the bleeding edge technology stack.\n\n[ng-describe-icon]: https://nodei.co/npm/ng-describe.png?downloads=true\n[ng-describe-url]: https://npmjs.org/package/ng-describe\n[ng-describe-ci-image]: https://travis-ci.org/kensho/ng-describe.png?branch=master\n[ng-describe-ci-url]: https://travis-ci.org/kensho/ng-describe\n[ng-describe-coverage-image]: https://coveralls.io/repos/kensho/ng-describe/badge.png\n[ng-describe-coverage-url]: https://coveralls.io/r/kensho/ng-describe\n[ng-describe-dependencies-image]: https://david-dm.org/kensho/ng-describe.png\n[ng-describe-dependencies-url]: https://david-dm.org/kensho/ng-describe\n[ng-describe-devdependencies-image]: https://david-dm.org/kensho/ng-describe/dev-status.png\n[ng-describe-devdependencies-url]: https://david-dm.org/kensho/ng-describe#info=devDependencies\n[ng-describe-codacy-image]: https://www.codacy.com/project/badge/25cb5d1410c7497cb057d887d1f3ea23\n[ng-describe-codacy-url]: https://www.codacy.com/public/kensho/ng-describe.git\n[semantic-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg\n[semantic-url]: https://github.com/semantic-release/semantic-release\n\n[quality-badge]: http://npm.packagequality.com/badge/ng-describe.png\n[quality-url]: http://packagequality.com/#?package=ng-describe\n\n[circle-icon]: https://circleci.com/gh/kensho/ng-describe.svg?style=svg\n[circle-url]: https://circleci.com/gh/kensho/ng-describe\n[dont-break]: https://github.com/bahmutov/dont-break\n\n\n\n* [Intro](#intro)\n* [Install](#install)\n* [API](#api)\n  * [Primary options](#primary-options)\n  * [Secondary options](#secondary-options)\n* [Examples](#examples)\n  * [Test value provided by a module](#test-value-provided-by-a-module)\n  * [Test a filter](#test-a-filter)\n  * [Test a service](#test-a-service)\n  * [Test controller and scope](#test-controller-and-scope)\n  * [Test directive](#test-directive)\n  * [Test controllerAs syntax](#test-controlleras-syntax)\n  * [Test controller instance in custom directive](#test-controller-instance-in-custom-directive)\n  * [Test 2 way binding](#test-2-way-binding)\n  * [beforeEach and afterEach](#beforeeach-and-aftereach)\n  * [Mocking](#mocking)\n    * [Mock value provided by a module](#mock-value-provided-by-a-module)\n    * [Angular services inside mocks](#angular-services-inside-mocks)\n    * [Mock $http.get](#mock-httpget)\n    * [Mock http responses](#mock-http-responses)\n  * [Spying](#spying)\n    * [Spy on injected methods](#spy-on-injected-methods)\n    * [Spy on injected function](#spy-on-injected-function)\n    * [Spy on 3rd party service injected some place else](#spy-on-3rd-party-service-injected-some-place-else)\n    * [Spy on mocked service](#spy-on-mocked-service)\n  * [Configure module](#configure-module)\n  * [Helpful failure messages](#helpful-failure-messages)\n* [Development](#development)\n  * [Updating dependencies](#updating-dependencies)\n* [Note to Jasmine users](#note-to-jasmine-users)\n* [Modules used](#modules-used)\n* [License](#license)\n\n\n## Intro\n\nUnit testing and mocking AngularJs requires a lot of boilerplate code:\n```js\ndescribe('typical test', function () {\n    var foo;\n    beforeEach(function () {\n        angular.mock.module('A');\n        // other modules\n    });\n    beforeEach(inject(function (_foo_) {\n        foo = _foo_;\n    }));\n    it('finally a test', function () {\n        expect(foo).toEqual('bar');\n    });\n});\n```\n\nng-describe makes testing simple modules a breeze.\nJust list which modules you would like to load, which values / services / etc.\nyou would like to inject and then start testing. Same test as above using ng-describe\nis much shorter and clearer:\n```js\nngDescribe({\n    modules: 'A',\n    tests: function (foo) {\n        it('finally a test', function () {\n            expect(foo).toEqual('bar');\n        });\n    }\n});\n```\nng-describe can inject dependencies, mock modules, set configs, create controllers, scopes, and\neven html fragments. For more details, continue reading. We also showed this library at AngularJS NYC\nmeetup, the slides are at [slides.com/bahmutov/ng-describe](http://slides.com/bahmutov/ng-describe).\n\n\n## Install\n\n`npm install ng-describe --save-dev`\n\nLoad ng-describe.js after angular and angular-mocks but before your specs, for example in Karma conf file.\n\n```js \n// karma.conf.js\nfiles: [\n    'node_modules/angular/angular.js',\n    'node_modules/angular-mocks/angular-mocks.js',\n    'node_modules/ng-describe/dist/ng-describe.js',\n    '\u003cyour source.js\u003e',\n    '\u003cyour specs.js\u003e'\n],\n```\n\nFile `dist/ng-describe.js` includes es5-shim and other dependencies needed by \nthe `ngDescribe` function.\n\n\n## API\n\nng-describe provides a single function `ngDescribe` that takes an options object.\n\n```js\nngDescribe({\n  // your options\n});\n```\n\nYou do not have to specify every option, there are reasonable defaults. We also tried to make\nthe API [user-friendly](http://glebbahmutov.com/blog/user-friendly-api/).\n\n`ngDescribe` returns itself, so you can chain multiple sets of specs easily\n\n```js\nngDescribe({\n  name: 'first suite'\n  ...\n})({\n  name: 'second suite'\n  ...\n});\n```\n\n### Primary options\n\n**name** - a string name for the spec, similar to BDD `describe(name, ...)`\n\n**modules** - list of modules to inject\n\n```js\nangular.module('A', []);\nangular.module('B', []);\nngDescribe({\n  name: 'modules example',\n  modules: ['A', 'B']\n});\n```\n\nIf you have a single module to inject, you can just use a string name without Array notation\n\n```js\nngDescribe({\n  name: 'single module',\n  modules: 'A'\n});\n```\n\n**inject** - list of dependencies to inject into unit tests. A single dependency can be just a string\nwithout Array notation. All dependencies will be exposed as properties of the `deps` argument to the\ntests callback\n\n```js\nangular.module('A', []).value('foo', 42);\nngDescribe({\n  name: 'inject example',\n  modules: 'A',\n  inject: ['foo', '$timeout'],\n  tests: function (deps) {\n    it('has foo', function () {\n      expect(deps.foo).toEqual(42);\n    });\n    it('has timeout service', function () {\n      expect(typeof deps.$timeout).toEqual('function');\n    });\n  }\n});\n```\n\n**tests** - callback function that contains actual specs. Think of this as equivalent to `describe` with\nall necessary Angular dependencies taken care of.\n\n```js\nngDescribe({\n  inject: ['$q', '$rootScope'],\n  tests: function (deps) {\n    it('injects $q', function () {\n      expect(typeof deps.$q).toEqual('function');\n    });\n    it('can be resolved', function () {\n      deps.$q.when(42).then(function (value) {\n        expect(value).toEqual(42);\n      });\n      // move promises along\n      deps.$rootScope.$digest();\n    });\n  }\n});\n```\n\n**Dependencies injection shortcut**\n\nYou can list the dependencies to be injected directly in the test callback.\n\n```js\nangular.module('shortcut', [])\n  .constant('foo', 'bar');\nngDescribe({\n  module: 'shortcut',\n  tests: function (foo) {\n    it('has constant', function () {\n      console.assert(foo === 'bar');\n    });\n  }\n});\n```\n\nYou can inject multiple providers, including built-in services. If the test callback argument\nis named `deps` or `dependencies` it will be assumed that you do NOT use the shortcut.\n\nThe shortcut was implemented using [changing named parameters trick][trick].\n\n[trick]: http://glebbahmutov.com/blog/changing-the-function-arguments-trick/\n\n**mocks** - top level mocks to be substituted into the tests.\nThe mocks override *any* injected dependencies among modules.\n\n```js\nngDescribe({\n  mocks: {\n    // each module to mock by name\n    moduleName1: {\n      // each dependency from moduleName1 to mock\n      dependencyName1: mockValue1,\n      dependencyName2: mockValue2\n      // the rest of moduleName1 is unchanged\n    },\n    moduleName2: {\n      // dependencies to mock in moduleName2\n    }\n  }\n});\n```\n\nFor more information see examples below.\n\n**controllers** - list of controllers by name that should be injected. Each controller\nis created with a new `$rootScope` instance.\n\n**NOTE: For each created controller, its SCOPE instance will be in the dependencies object.**\n\n```js\nangular.module('D', [])\n  .controller('dController', function ($scope) {\n    $scope.foo = 'foo';\n  });\nngDescribe({\n  modules: 'D',\n  controllers: 'dController',\n  tests: function (deps) {\n    it('is a scope for controller', function () {\n      expect(typeof deps.dController).toEqual('object');\n      // deps.dController is the $scope object injected into dController\n      expect(deps.dController.foo).toEqual('foo');\n    });\n  }\n});\n```\n\n**element** - HTML fragment string for testing custom directives and DOM updates.\n\n```js\nngDescribe({\n  element: '\u003cmy-foo bar=\"baz\"\u003e\u003c/my-foo\u003e'\n});\n```\n\nThe compiled `angular.element` will be injected into the dependencies object under `element` property.\nSee examples below for more information. The compilation will create a new scope object too.\n\n**parentScope** - when creating HTML fragment, copies properties from this object into the\nscope. The returned dependencies object will have `deps.parentScope` that is the new scope.\n\n```js\n// myFoo directive uses isolate scope for example\nngDescribe({\n  element: '\u003cmy-foo bar=\"baz\"\u003e\u003c/my-foo\u003e',\n  parentScope: {\n    baz: 42\n  },\n  tests: function (deps) {\n    it('baz -\u003e bar', function () {\n      deps.parentScope.baz = 100;\n      deps.$rootScope.$apply();\n      expect(deps.element.isolateScope().bar).toEqual(100);\n    });\n  }\n});\n```\n\nSee \"2 way binding\" example below.\n\n**configs** - object with modules that have provider that can be used to inject\nrun time settings.\nSee *Update 1* in\n[Inject valid constants into Angular](http://glebbahmutov.com/blog/inject-valid-constants-into-angular/)\nblog post and examples below.\n\n### Secondary options\n\n**verbose** - flag to print debug messages during execution\n\n**only** - flag to run this set of tests and skip the rest. Equivalent to\n[ddescribe or describe.only](http://glebbahmutov.com/blog/focus-on-karma-test/).\n\n```js\nngDescribe({\n  name: 'run this module only',\n  only: true\n});\n```\n\n**skip** - flag to skip this group of specs. Equivalent to `xdescribe` or `describe.skip`.\nCould be a string message explaining the reason for skipping the spec.\n\n**exposeApi** - expose low-level ngDescribe methods\n\nThe `tests` callback will get the second argument, which is an object with the following methods\n\n    {\n      setupElement: function (elementHtml),\n      setupControllers: function (controllerNames)\n    }\n\nYou can use `setupElement` to control when to create the element.\nFor example, instead of creating element right away, expose element factory so that you can create\nan element *after* running a `beforeEach` block. Useful for setting up mock backend before creating\nan element.\n\n```js\nngDescribe({\n  exposeApi: true,\n  inject: '$httpBackend',\n  // no element option\n  tests: function (deps, describeApi) {\n    beforeEach(function () {\n      deps.$httpBackend\n        .expectGET('/api/foo/bar').respond(500);\n    });\n    beforeEach(function () {\n      // now create an element ourselves\n      describeApi.setupElement('\u003cstudy-flags /\u003e');\n    });\n    it('created an element', function () {\n      la(check.has(deps.element));\n    });\n  });\n});\n```\n\nSee the spec in [test/expose-spec.js](test/expose-spec.js)\n\nOr you can use `setupControllers` to create controller objects AFTER setting up your spies.\n\n```js\nangular.module('BroadcastController', [])\n  .controller('broadcastController', function broadcastController($rootScope) {\n    $rootScope.$broadcast('foo');\n  });\n```\n\nWe need to listen for the `foo` broadcast inside a unit test before creating the controller.\nIf we let `ngDescribe` create the \"broadcastController\" it will be too late. Instead we\ncan tell the `ngDescribe` to expose the low-level api and then we create the controllers when\nwe are ready\n\n```js\nngDescribe({\n  name: 'spy on controller init',\n  modules: 'BroadcastController',\n  inject: '$rootScope',\n  exposeApi: true,\n  tests: function (deps, describeApi) {\n    it('can catch the broadcast in controller init', function (done) {\n      var heardFoo;\n      deps.$rootScope.$on('foo', function () {\n        heardFoo = true;\n        done();\n      });\n      describeApi.setupControllers('broadcastController');\n    });\n  }\n});\n```\n\nSee the spec in [test/controller-init-spec.js](test/controller-init-spec.js)\n\n**http** - shortcut for specifying mock HTTP responses,\nbuilt on top of [$httpBackend](https://docs.angularjs.org/api/ngMock/service/$httpBackend).\nEach GET request will be mapped to `$httpBackend.whenGET` for example. You can provide\ndata, response code + data pair, response code + data + headers and optionally statusText\nor custom function to return something using custom logic.\nIf you use `http` property, then the injected dependencies will have `http` object that\nyou can flush (it is really `$httpBackend` object).\n\n```js\nngDescribe({\n  inject: '$http', // for making test calls\n  http: {\n    get: {\n      '/my/url': 42, // status 200, data 42\n      '/my/other/url': [202, 42], // status 202, data 42,\n      '/my/smart/url': function (method, url, data, headers) {\n        return [500, 'something is wrong'];\n      } // status 500, data \"something is wrong\"\n    },\n    post: {\n      '/my/url': '/my/url': [201, {message: 'ok'}, {Location: '/new/url'}, 'this is the new response'], // status data, headers and statusText\n      // same format as GET\n    }\n  },\n  tests: function (deps) {\n    it('responds', function (done) {\n      deps.$http.get('/my/other/url')\n        .then(function (response) {\n          // expect\n          // response.status = 202\n          // response.data = 42\n          done();\n        });\n      deps.http.flush();\n    });\n  }\n});\n```\nAll standard methods should be supported (`get`, `head`, `post`, `put`, `delete`, `jsonp` and `patch`).\n\nEach of the methods can return a function that returns an configuration object, see [mock http](#mock-http).\n\n**step** - shortcut for running the digest cycle and mock http flush\n\n```js\ntests: function (deps) {\n  it('runs the digest cycle', function (done) {\n    $q.when(42).finally(done);\n    deps.step();\n    // same as deps.$rootScope.$digest();\n  });\n}\n```\n\nAlso flushes the mock http backend\n\n```js\nhttp: {}\ntests: function (deps) {\n  it('returns expected result', function (done) {\n    deps.$http.get(...)\n      .then(...)\n      .finally(done);\n    deps.step();\n    // same as deps.http.flush();\n  });\n}\n```\n\nAlso flushed the `$timeout` service\n\n```js\nngDescribe({\n  inject: '$timeout',\n  tests: function (deps) {\n    it(function () {\n      deps.$timeout(...)\n      deps.step();\n      // same as deps.$timeout.flush()\n    })\n  }\n})\n```\n\n**root** - alternative context for BDD callbacks\n\nImagine we are loading Angular and ngDescribe in a synthetic browser environment (like \n[jsdom](https://www.npmjs.com/package/jsdom)). ngDescribe attaches itself to synthetic `window`\nobject, but the test framework callbacks are attached to `global` object, not `window`.\nBy passing an alternative object, we allow ngDescribe to discover `it`, `beforeEach`, etc.\n\n```js\n// load ngDescribe in jsdom under Node\nwindow.ngDescribe({\n  root: global,\n  tests: function (deps) {\n    ...\n  }\n})\n```\n\nSee repo [ng-describe-jsdom](https://gitlab.com/bahmutov/ng-describe-jsdom) for actual\nexample that tests Angular without a browser, only a synthetic emulation.\n\n\n## Examples\n\nMost examples use use the `la` assertion from the\n[lazy-ass](https://github.com/bahmutov/lazy-ass) library and *done* callback argument\nfrom [Mocha](http://visionmedia.github.io/mocha/) testing framework.\n\nAlso, note that the dependencies object is filled **only** inside the unit test callbacks `it` and\nsetup helpers `beforeEach` and `afterEach`\n\n```js\nngDescribe({\n  inject: 'foo',\n  tests: function (deps) {\n    // deps is an empty object here\n    beforeEach(function () {\n      // deps object has 'foo'\n    });\n    // deps is an empty object here\n    it(function () {\n      // deps object has 'foo'\n    });\n    // deps is an empty object here\n    afterEach(function () {\n      // deps object has 'foo'\n    });\n  }\n});\n```\n\n### Test value provided by a module\n\n```js\n// A.js\nangular.module('A', [])\n  .value('foo', 'bar');\n// A-spec.js\nngDescribe({\n  name: 'test value',\n  modules: 'A',\n  inject: 'foo',\n  tests: function (deps) {\n    // deps object has every injected dependency as a property\n    it('has correct value foo', function () {\n      la(deps.foo === 'bar');\n    });\n  }\n});\n```\n\n### Test a filter\n\nWe can easily test a built-in or custom filter function\n\n```js\nngDescribe({\n  name: 'built-in filter',\n  inject: '$filter',\n  tests: function (deps) {\n    it('can convert to lowercase', function () {\n      var lowercase = deps.$filter('lowercase');\n      la(lowercase('Foo') === 'foo');\n    });\n  }\n});\n```\n\n### Test a service\n\nWe can inject a service to test using the same approach. You can even use multiple specs inside `tests` callback.\n\n```js\n// B.js\nangular.module('B', ['A'])\n  .service('addFoo', function (foo) {\n    return function (str) {\n      return str + foo;\n    };\n  });\n// B-spec.js\nngDescribe({\n  name: 'service tests',\n  modules: 'B',\n  inject: 'addFoo',\n  tests: function (deps) {\n    it('is a function', function () {\n      la(typeof deps.addFoo === 'function');\n    });\n    it('appends value of foo to any string', function () {\n      var result = deps.addFoo('x');\n      la(result === 'xbar');\n    });\n  }\n});\n```\n\n### Test controller and scope\n\nWe can easily create instances of controller functions and scope objects.\nIn this example we also inject `$timeout` service to speed up delayed actions\n(see [Testing Angular async stuff](http://glebbahmutov.com/blog/testing-angular-async-stuff/)).\n\n```js\nangular.module('S', [])\n  .controller('sample', function ($timeout, $scope) {\n    $scope.foo = 'foo';\n    $scope.update = function () {\n      $timeout(function () {\n        $scope.foo = 'bar';\n      }, 1000);\n    };\n  });\nngDescribe({\n  name: 'timeout in controller',\n  modules: 'S',\n  // inject $timeout so we can flush the timeout queue\n  inject: ['$timeout'],\n  controllers: 'sample',\n  tests: function (deps) {\n    // deps.sample = $scope object injected into sample controller\n    it('has initial values', function () {\n      la(deps.sample.foo === 'foo');\n    });\n    it('updates after timeout', function () {\n      deps.sample.update();\n      deps.$timeout.flush();\n      la(deps.sample.foo === 'bar');\n    });\n  }\n});\n```\n\n### Test directive\n\n```js\nangular.module('MyFoo', [])\n  .directive('myFoo', function () {\n    return {\n      restrict: 'E',\n      replace: true,\n      template: '\u003cspan\u003e{{ bar }}\u003c/span\u003e'\n    };\n  });\nngDescribe({\n  name: 'MyFoo directive',\n  modules: 'MyFoo',\n  element: '\u003cmy-foo\u003e\u003c/my-foo\u003e',\n  tests: function (deps) {\n    it('can update DOM using binding', function () {\n      la(check.has(deps, 'element'), 'has compiled element');\n      var scope = deps.element.scope();\n      scope.bar = 'bar';\n      scope.$apply();\n      la(deps.element.html() === 'bar');\n    });\n  }\n});\n```\n\n### Test controllerAs syntax\n\nIf you use `controllerAs` syntax without any components (see [Binding to ...][binding] post or\n[Separate ...][separate]), then you can still test it quickly\n\n```js\nangular.module('H', [])\n  .controller('hController', function () {\n    // notice we attach properties to the instance, not to the $scope\n    this.foo = 'foo';\n  });\n  ngDescribe({\n    module: 'H',\n    element: '\u003cdiv ng-controller=\"hController as ctrl\"\u003e{{ ctrl.foo }}\u003c/div\u003e',\n    tests: function (deps) {\n      it('created controller correctly', function () {\n        var compiledHtml = deps.element.html();\n        // 'foo'\n      });\n      it('changes value', function () {\n        var ctrl = deps.element.controller();\n        // { foo: 'foo' }\n        ctrl.foo = 'bar';\n        deps.element.scope().$apply();\n        var compiledHtml = deps.element.html();\n        // 'bar'\n      });\n    }\n  });\n```\n\n[binding]: http://blog.thoughtram.io/angularjs/2015/01/02/exploring-angular-1.3-bindToController.html\n[separate]: http://glebbahmutov.com/blog/separate-model-from-view-in-angular/\n\n### Test controller instance in custom directive\n\nIf you add methods to the controller inside custom directive, use `controllerAs` syntax to\nexpose the controller instance.\n\n```js\nangular.module('C', [])\n  .directive('cDirective', function () {\n    return {\n      controllerAs: 'ctrl', // puts controller instance onto scope as ctrl\n      controller: function ($scope) {\n        $scope.foo = 'foo';\n        this.foo = function getFoo() {\n          return $scope.foo;\n        };\n      }\n    };\n  });\nngDescribe({\n  name: 'controller for directive instance',\n  modules: 'C',\n  element: '\u003cc-directive\u003e\u003c/c-directive\u003e',\n  tests: function (deps) {\n    it('has controller', function () {\n      var scope = deps.element.scope(); // grabs scope\n      var controller = scope.ctrl; // grabs controller instance\n      la(typeof controller.foo === 'function');\n      la(controller.foo() === 'foo');\n      scope.foo = 'bar';\n      la(controller.foo() === 'bar');\n    });\n  }\n});\n```\n\n### Test 2 way binding\n\nIf a directive implements isolate scope, we can configure parent scope separately.\n\n```js\nangular.module('IsolateFoo', [])\n  .directive('aFoo', function () {\n    return {\n      restrict: 'E',\n      replace: true,\n      scope: {\n        bar: '='\n      },\n      template: '\u003cspan\u003e{{ bar }}\u003c/span\u003e'\n    };\n  });\n```\n\nWe can use `element` together with `parentScope` property to set initial values.\n\n```js\nngDescribe({\n  modules: 'IsolateFoo',\n  element: '\u003ca-foo bar=\"x\"\u003e\u003c/a-foo\u003e',\n  parentScope: {\n    x: 'initial'\n  },\n  tests: function (deps) {\n    it('has correct initial value', function () {\n      var scope = deps.element.isolateScope();\n      expect(scope.bar).toEqual('initial');\n    });\n  }\n});\n```\n\nWe can change parent's values to observe propagation into the directive\n\n```js\n// same setup\nit('updates isolate scope', function () {\n  deps.parentScope.x = 42;\n  deps.$rootScope.$apply();\n  var scope = deps.element.isolateScope();\n  expect(scope.bar).toEqual(42);\n});\n```\n\n### beforeEach and afterEach\n\nYou can use multiple `beforeEach` and `afterEach` inside `tests` function.\n\n```js\nngDescribe({\n  name: 'before and after example',\n  modules: ['A'],\n  inject: ['foo'],\n  tests: function (deps) {\n    var localFoo;\n    beforeEach(function () {\n      // dependencies are already injected\n      la(deps.foo === 'bar');\n      localFoo = deps.foo;\n    });\n    it('has correct value foo', function () {\n      la(localFoo === 'bar');\n    });\n    afterEach(function () {\n      la(localFoo === 'bar');\n      // dependencies are still available\n      la(deps.foo === 'bar');\n    });\n  }\n});\n```\n\nThis could be useful for setting up additional mocks, like `$httpBackend`.\n\n```js\nangular.module('apiCaller', [])\n  .service('getIt', function ($http) {\n    return function () {\n      return $http.get('/my/url');\n    };\n  });\nngDescribe({\n  name: 'http mock backend example',\n  modules: ['apiCaller'],\n  inject: ['getIt', '$httpBackend'],\n  tests: function (deps) {\n    beforeEach(function () {\n      deps.$httpBackend.expectGET('/my/url').respond(200, 42);\n    });\n    it('returns result from server', function (done) {\n      deps.getIt().then(function (response) {\n        la(response \u0026\u0026 response.status === 200);\n        la(response.data === 42);\n        done();\n      });\n      deps.$httpBackend.flush();\n    });\n    afterEach(function () {\n      deps.$httpBackend.verifyNoOutstandingRequest();\n      deps.$httpBackend.verifyNoOutstandingExpectation();\n    });\n  }\n});\n```\n\n**Note** if you use `beforeEach` block with `element`, the `beforeEach` runs *before* the element\nis created. This gives you a chance to setup mocks before running the element and possibly making calls.\nIf you really want to control when an element is created use `exposeApi` option \n(see [Secondary options](#secondary-options)).\n\n### Mocking\n\n#### Mock value provided by a module\n\nOften during testing we need to mock something provided by a module, even if it is \npassed via dependency injection. ng-describe makes it very simple. List all modules with values \nto be mocked in `mocks` object property.\n\n```js\n// C.js\nangular.module('C', ['A'])\n  .service('getFoo', function (foo) {\n    // foo is provided by module A\n    return function getFoo() {\n      return foo;\n    };\n  });\n// C-spec.js\nngDescribe({\n  name: 'test C with mocking top level',\n  modules: ['C'],\n  inject: ['getFoo'],\n  mocks: {\n    // replace C.getFoo with mock function that returns 11\n    C: {\n      getFoo: function () {\n        return 11;\n      }\n    }\n  },\n  verbose: false,\n  tests: function (deps) {\n    it('has mock injected value', function () {\n      var result = deps.getFoo();\n      la(result === 11, 'we got back mock value', result);\n    });\n  }\n});\n```\n\nRemember when making mocks, it is always `module name : provider name : mocked property name`\n\n```js\nmocks: {\n  'module name': {\n    'mocked provider name': {\n      'mocked value name'\n    }\n  }\n}\n```\n\nNote: the mocked values are injected using `$provider.constant` call to be able to override both\nvalues and constants\n\n```js\nangular.module('A10', [])\n  .constant('foo', 'bar');\nngDescribe({\n  modules: 'A10',\n  mock: {\n    A10: {\n      foo: 42\n    }\n  },\n  inject: 'foo',\n  tests: function (deps) {\n    it('has correct constant foo', function () {\n      expect(deps.foo).toEqual(42);\n    });\n  }\n});\n```\n\nYou can even mock part of the module itself and use mock value in other parts via injection\n\n```js\nangular.module('LargeModule', [])\n  .constant('foo', 'foo')\n  .service('getFoo', function (foo) {\n    return function getFoo() {\n      return foo;\n    };\n  });\nngDescribe({\n  name: 'mocking part of the module itself',\n  modules: 'LargeModule',\n  inject: 'getFoo',\n  mock: {\n    LargeModule: {\n      foo: 'bar'\n    }\n  },\n  tests: function (deps) {\n    it('service injects mock value', function () {\n      la(deps.getFoo() === 'bar', 'returns mock value');\n    });\n  }\n});\n```\n\n#### Angular services inside mocks\n\nYou can use other injected dependencies inside mocked functions, using\ninjected values and free parameters.\n\n```js\nngDescribe({\n  inject: ['getFoo', '$rootScope'],\n  mocks: {\n    C: {\n      // use angular $q service in the mock function\n      // argument \"value\" remains free\n      getFoo: function ($q, value) {\n        return $q.when(value);\n      }\n    }\n  },\n  tests: function (deps) {\n    it('injected $q into mock', function (done) {\n      deps.getFoo('foo').then(function (result) {\n        expect(result).toEqual('foo');\n        done();\n      });\n      deps.$rootScope.$apply(); // resolve promise\n    });\n  }\n});\n```\n\n#### Mock $http.get\n\nOften we need some dummy response from `$http.get` method. We can use mock `httpBackend` \nor mock the `$http` object. For example to always return mock value when making any GET request,\nwe can use\n\n```js\nmocks: {\n  ng: {\n    $http: {\n      get: function ($q, url) {\n        // inspect url if needed\n        return $q.when({\n          data: {\n            life: 42\n          }\n        });\n      }\n    }\n  }\n}\n```\n\n`$http` service returns a promise that resolves with a *response* object. The actual result to send\nis placed into the `data` property, as I show here.\n\n#### Mock http responses\n\nYou can use a shortcut to define mock HTTP responses via `$httpBackend` module. For example,\nyou can define static responses.\n\n```js\nngDescribe({\n  http: {\n    get: {\n      '/some/url': 42,\n      '/some/other/url': [500, 'something went wrong']\n    },\n    post: {\n      // you can use custom functions too\n      '/some/post/url': function (method, url, data, headers) {\n        return [200, 'ok'];\n      }\n    }\n  }\n});\n```\nAll HTTP methods are supported (`get`, `post`, `delete`, `put`, etc.).\n\nYou can also get a function that would return a config object.\n\n```js\nvar mockGetApi = {\n  '/some/url': 42\n};\nmockGetApi['/some/other/url'] = [500, 'not ok'];\nngDescribe({\n  http: {\n    get: mockGetApi\n  }\n});\n```\n\nYou can use `deps.http.flush()` to move the http responses along.\n\nYou can return the entire http mock object from a function, or combine objects with functions.\n\n```js\nfunction constructMockApi() {\n  return {\n    get: function () {\n      return { '/my/url': 42 };\n    },\n    post: {\n      '/my/other/url': [200, 'nice']\n    }\n  };\n}\nngDescribe({\n  http: constructMockApi,\n  test: function (deps) {\n    ...\n  } \n});\n```\n\nYou can use exact query arguments too\n\n```js\nhttp: {\n  get: {\n    '/foo/bar?search=value': 42,\n    '/foo/bar?search=value\u0026something=else': 'foo'\n  }\n}\n// $http.get('/foo/bar?search=value') will resolve with value 42\n// $http.get('/foo/bar?search=value\u0026something=else') will resolve with value 'foo'\n```\n\nor you can build the query string automatically by passing `params` property in the request config\nobjet\n\n```js\nhttp: {\n  get: {\n    '/foo/bar?search=value\u0026something=else': 'foo'\n  }\n}\n// inside the unit test\nvar config = {\n  params: {\n    search: 'value',\n    something: 'else'\n  }\n};\n$http.get('/foo/bar', config).then(function (response) {\n  // response.data = 'foo'\n});\n```\n\n**note** the `http` mocks are defined using `$httpBack.when(method, ...)` calls, \nwhich are looser than `$httpBackend.expect(method, ...)`, \nsee [ngMock/$httpBackend](https://docs.angularjs.org/api/ngMock/service/$httpBackend).\n\n### Spying\n\n#### Spy on injected methods\n\nOne can quickly spy on injected services (or other methods) using [sinon.js](http://sinonjs.org/) \nsimilarly to [spying on the regular JavaScript methods](http://glebbahmutov.com/blog/spying-on-methods/).\n\n* Include a browser-compatible combined [sinon.js build](http://sinonjs.org/releases/sinon-1.12.1.js) \ninto the list of loaded Karma files.\n* Setup spy in the `beforeEach` function. Since every injected service is a method on the `deps`\nobject, the setup is a single command.\n* Restore the original method in `afterEach` function.\n\n```js\n// source code\nangular.module('Tweets', [])\n  .service('getTweets', function () {\n    return function getTweets(username) {\n      console.log('returning # of tweets for', username);\n      return 42;\n    };\n  });\n```\n\n```js\n// spec\nngDescribe({\n  name: 'spying on Tweets getTweets service',\n  modules: 'Tweets',\n  inject: 'getTweets',\n  tests: function (deps) {\n    beforeEach(function () {\n      sinon.spy(deps, 'getTweets');\n    });\n    afterEach(function () {\n      deps.getTweets.restore();\n    });\n    it('calls getTweets service', function () {\n      var n = deps.getTweets('foo');\n      la(n === 42, 'resolved with correct value');\n      la(deps.getTweets.called, 'getTweets was called (spied using sinon)');\n      la(deps.getTweets.firstCall.calledWith('foo'));\n    });\n  }\n});\n```\n\n#### Spy on injected function\n\nYou can inject a function, but use a [Sinon spy](http://sinonjs.org/docs/#spies) instead\nof the injected function to get additional information. For example, to spy on the `$filter uppercase`,\nwe can use the following code.\n\n```js\nngDescribe({\n  name: 'spying on a filter',\n  inject: '$filter',\n  tests: function (deps) {\n    /*\n      to spy on a injected filter, need to grab the actual filter function\n      and then create a spy\n    */\n    // _uppercase = angular uppercase $filter\n    // uppercase = spy on the _uppercase\n    var _uppercase, uppercase;\n    beforeEach(function () {\n      _uppercase = deps.$filter('uppercase');\n      uppercase = sinon.spy(_uppercase);\n    });\n    it('converts string to uppercase', function () {\n      var result = uppercase('foo');\n      la(result === 'FOO', 'converted string to uppercase', result);\n      la(uppercase.calledOnce, 'uppercase was called once');\n      la(uppercase.calledWith('foo'));\n    });\n  }\n});\n```\n\n#### Spy on 3rd party service injected some place else\n\nLet us say you need to verify that the `$interval` service injected in the module under test\nwas called. It is a little verbose to verify from the unit test. We must mock the `$interval`\nwith our function and then call the actual `$interval` from the module `ng` to provide the\nsame functionality.\n\nSource code we are trying to unit test\n\n```js\nangular.module('IntervalExample', [])\n  .service('numbers', function ($interval, $rootScope) {\n    return function emitNumbers(delay, n) {\n      var k = 0;\n      $interval(function () {\n        $rootScope.$emit('number', k);\n        k += 1;\n      }, 100, n);\n    };\n  });\n```\n\nIn the unit test we will mock `$interval` service for module `IntervalExample`\n\n```js\n// unit test start\nvar intervalCalled;\nngDescribe({\n  name: 'spying on $interval',\n  module: 'IntervalExample',\n  inject: ['numbers', '$rootScope'],\n  verbose: false,\n  only: false,\n  mocks: {\n    IntervalExample: {\n      $interval: function mockInterval(fn, delay, n) {\n        var injector = angular.injector(['ng']);\n        var $interval = injector.get('$interval');\n        intervalCalled = true;\n        return $interval(fn, delay, n);\n      }\n    }\n  },\n  tests: function (deps) {\n    // unit test goes here\n  }\n});\n```\n\nA unit test just calls the `numbers` function and then checks the variable `intervalCalled`\n\n```js\nit('emits 3 numbers', function (done) {\n  deps.$rootScope.$on('number', function (event, k) {\n    if (k === 2) {\n      done();\n    }\n  });\n  // emit 3 numbers with 100ms interval\n  deps.numbers(100, 3);\n  la(intervalCalled, 'the $interval was called somewhere');\n});\n```\n\nYou can see the unit test in file [test/spying-on-interval-spec.js](test/spying-on-interval-spec.js).\n\n#### Spy on mocked service\n\nIf we mock an injected service, we can still spy on it, just like as if we were spying on the\nregular service. For example, let us take the same method as above and mock it.\n\n```js\nangular.module('Tweets', [])\n  .service('getTweets', function () {\n    return function getTweets(username) {\n      return 42;\n    };\n  });\n```\n\nThe mock will return a different number.\n\n```js\nngDescribe({\n  name: 'spying on mock methods',\n  inject: 'getTweets',\n  mocks: {\n    Tweets: {\n      getTweets: function (username) {\n        return 1000;\n      }\n    }\n  },\n  tests: function (deps) {\n    beforeEach(function () {\n      sinon.spy(deps, 'getTweets');\n    });\n    afterEach(function () {\n      deps.getTweets.restore();\n    });\n    it('calls mocked getTweets service', function () {\n      var n = deps.getTweets('bar');\n      la(n === 1000, 'resolved with correct value from the mock service');\n      la(deps.getTweets.called,\n        'mock service getTweets was called (spied using sinon)');\n      la(deps.getTweets.firstCall.calledWith('bar'),\n        'mock service getTweets was called with expected argument');\n    });\n  }\n});\n```\n\n### Configure module\n\nIf you use a separate module with namesake provider to pass configuration into the modules\n(see [Inject valid constants into Angular](http://glebbahmutov.com/blog/inject-valid-constants-into-angular/)),\nyou can easily configure these modules.\n\n```js\nangular.module('App', ['AppConfig'])\n  .service('foo', function (AppConfig) {\n    return function foo() {\n      return GConfig.bar;\n    };\n  });\n// config module has provider with same name\nangular.module('AppConfig', [])\n  .provider('AppConfig', function () {\n    var config = {};\n    return {\n      set: function (settings) {\n        config = settings;\n      },\n      $get: function () {\n        return config;\n      }\n    };\n  });\n// spec file\nngDescribe({\n  name: 'config module example',\n  modules: 'App',\n  inject: 'foo',\n  configs: {\n    // every config module will be loaded automatically\n    AppConfig: {\n      bar: 'boo!'\n    }\n  },\n  tests: function (deps) {\n    it('foo has configured bar value', function () {\n      expect(deps.foo()).toEqual('boo!');\n    });\n  }\n});\n```\n\nYou can configure multiple modules at the same time. Note that during the configuration\nAngular is yet to be loaded. Thus you cannot use Angular services inside the configuration blocks.\n\n### Helpful failure messages\n\nng-describe works inside [helpDescribe function](https://github.com/bahmutov/lazy-ass-helpful#lazy-ass-helpful-bdd),\nproducing meaningful error messages on failure (if you use [lazy assertions](https://github.com/bahmutov/lazy-ass)).\n\n```js\nhelpDescribe('ngDescribe inside helpful', function () {\n  ngDescribe({\n    name: 'example',\n    tests: function () {\n      it('gives helpful error message', function () {\n        var foo = 2, bar = 3;\n        la(foo + bar === 4); // wrong on purpose\n      });\n    }\n  });\n});\n```\nwhen this test fails, it generates meaningful message with all relevant information: the expression\nthat fails `foo + bar === 4` and runtime values of `foo` and `bar`.\n\n    PhantomJS 1.9.7 (Mac OS X) \n    ட ngDescribe inside helpful \n      ட example \n        ட ✘ gives helpful error message FAILED\n      Error: condition [foo + bar === 4] foo: 2 bar: 3\n          at lazyAss (/ng-describe/node_modules/lazy-ass/index.js:57)\n    PhantomJS 1.9.7 (Mac OS X): Executed 37 of 38 (1 FAILED) (skipped 1) (0.053 secs / 0.002 secs)\n\n\n## Development\n\nTo build the README document, run unit tests and linter\n\n    npm run build\n\nTo run all unit tests (against different Angular versions)\n\n    npm test\n\nTo keep a watch and rerun build + lint + tests on source file change\n\n    npm run watch\n\nFor now, all source is in a single `ng-describe.js` file, while the documentation\nis generated from Markdown files in the `docs` folder\n\nTo just run karma unit tests via Grunt plugin\n\n    npm run karma\n\nIf you have Karma runner installed globally you can run all the unit tests yourself ones\n\n    karma start --single-run=true test/karma.conf.js\n\n### Updating dependencies\n\nThis project uses a lot of 3rd party dependencies that constantly get out of date.\nTo reliably update dependencies to the latest working versions, we use\n[next-update](https://github.com/bahmutov/next-update). There is already a script command\n\n    npm run update-dependencies\n\nYou can upgrade a particular dependency by adding \"-m \u003cname\u003e\", for example\n\n    npm run update-dependencies -- -m jscs\n\nIf you use [npm-quick-run](https://github.com/bahmutov/npm-quick-run) you can use shorthand\n\n    nr u -m jscs\n\n\n## Note to Jasmine users\n\nWe got very tired of fighting bugs in the [Jasmine](http://jasmine.github.io/) test framework.\nFrom the broken order of `afterEach` callbacks to the `afterAll` not firing at all - the work arounds\nwe had to write quickly becamse insane. Thus we \n[recommend Mocha](https://glebbahmutov.com/blog/picking-javascript-testing-framework/) testing\nframework - fast, simple and seems to not suffer from any bugs. You do need your own assertion\nframework, we use [lazy-ass](https://github.com/bahmutov/lazy-ass) and a library\nof predicates [check-more-types](https://github.com/kensho/check-more-types).\n\n\n\n## Modules used\n* [check-more-types](https://github.com/kensho/check-more-types) - Large collection of predicates.\n* [lazy-ass](https://github.com/bahmutov/lazy-ass) - Lazy assertions without performance penalty\n\n\n## License\n\nAuthor: Kensho \u0026copy; 2014\n\n* [@kensho](https://twitter.com/kensho)\n* [kensho.com](http://kensho.com)\n\nSupport: if you find any problems with this library,\n[open issue](https://github.com/kensho/ng-describe/issues) on Github\n\n\nThe MIT License (MIT)\n\nCopyright (c) 2014 Kensho\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkensho%2Fng-describe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkensho%2Fng-describe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkensho%2Fng-describe/lists"}