{"id":20229273,"url":"https://github.com/hisco/unit-test-class","last_synced_at":"2025-10-30T02:52:01.408Z","repository":{"id":57386448,"uuid":"134719699","full_name":"hisco/unit-test-class","owner":"hisco","description":"Easy mock your es6 + classes.","archived":false,"fork":false,"pushed_at":"2020-05-23T11:58:41.000Z","size":164,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-04-24T23:22:41.980Z","etag":null,"topics":["class","constructor","mock","spies","unit-testing"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/unit-test-class","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/hisco.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":"2018-05-24T13:32:10.000Z","updated_at":"2020-07-30T12:53:01.000Z","dependencies_parsed_at":"2022-09-05T05:00:24.109Z","dependency_job_id":null,"html_url":"https://github.com/hisco/unit-test-class","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hisco%2Funit-test-class","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hisco%2Funit-test-class/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hisco%2Funit-test-class/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hisco%2Funit-test-class/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hisco","download_url":"https://codeload.github.com/hisco/unit-test-class/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241670722,"owners_count":20000456,"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":["class","constructor","mock","spies","unit-testing"],"created_at":"2024-11-14T07:35:06.486Z","updated_at":"2025-10-30T02:51:56.373Z","avatar_url":"https://github.com/hisco.png","language":"JavaScript","readme":"# Unit test class\n\n[![Greenkeeper badge](https://badges.greenkeeper.io/hisco/unit-test-class.svg)](https://greenkeeper.io/)\n\n[![NPM Version][npm-image]][npm-url]\n[![Build Status][travis-image]][travis-url]\n[![Test Coverage][coveralls-image]][coveralls-url]\n\n  Easy mock your es6 + classes.\n\nUnit tests classes was created from a great conflict, the desire to create great stuff with great tests coverage VS the time invested to unit testing classes.\n\nUnit test definition from Wiki:\n`Intuitively, one can view a unit as the smallest testable part of an application. In procedural programming, a unit could be an entire module, but it is more commonly an individual function or procedure. In object-oriented programming, a unit is often an entire interface, such as a class, but could be an individual method.`\n\nUnit test class module addresses the last part `could be an individual method`.\n\nThis module is completely framework agnostic and was build without any framework dependencies, to make sure you can use all of these cool frameworks:\n  * Jasmine\n  * Mocha\n  * AVA\n  * Tape\n  * Jest\n  * Sinon\n  * And more!\n\n## Test faster\nWriting unit-test taked a lot of time.\nMainlly because we need to re-create isolated scenarios where we can test the smallest parts.\n\"Unit test class\" - is here to help by removing all the tedious boilerplate code from your unit tests.\n\n## Test better\nIt's sometimes very difficult to gain total isolation to the unit your testing.\nTherefore, it's very common and wrong some developers chose to test a method by not noticing it's inflaunced by the other method result _They have creted an integration test, not unit test_ .\n\n##Api\n\n###TL;DR\n```js\n//You have a class\n\nclass YourClass{\n    constructor(name){\n        this._name = name;\n    }\n    get myName(){\n        return `My name is ${this._name}`\n    }\n    hi(userName){\n        if (userName)\n            this.log(`Hi ${userName},${this.myName}`);\n        else\n            this.log('Hi, What\\'s you name?')\n    }\n    log(string){\n        console.log(string)\n    }\n}\n//You can test it like that\nconst chai = require('chai');\nchai.use(require('chai-spies'));\nconst expect = chai.expect;\n\nconst {MockedClassService} = require('unit-test-class');\n\nconst mockService = new MockedClassService(chai.spy);\ndescribe('YourClass' , ()=\u003e{\n    const YourClassFactory = mockService.mock(YourClass);\n    let mockView;\n    describe('#hi' , ()=\u003e{\n        let getMyName;\n        beforeEach(()=\u003e{\n            getMyName = chai.spy(()=\u003e'myName');\n            mockView = YourClassFactory.test('hi').spies({\n                    get myName(){\n                        return getMyName();\n                    }\n                })\n                .create();\n            \n        })\n        it('Should log with user name' , ()=\u003e{\n            const {instance , spies} = mockView;\n\n            instance.hi('bob'); \n\n            //Spy on a getter\n            expect(getMyName).to.have.been.called();\n\n            //Spy on internal method\n            expect(instance.log).to.have.been.called.with('Hi bob,myName');\n            //OR alterntivally\n            expect(spies.log_function).to.have.been.called.with('Hi bob,myName');\n        })\n        it('Should ask user name' , ()=\u003e{\n            const {instance , spies} = mockView;\n\n            instance.hi(); \n\n            //Spy on a getter\n            expect(getMyName).to.not.have.been.called();\n\n            //Spy on internal method\n            expect(instance.log).to.have.been.called.with('Hi, What\\'s you name?');\n            //OR alterntivally\n            expect(spies.log_function).to.have.been.called.with('Hi, What\\'s you name?');\n        })\n    })\n});\n```\n\n### MockedClassService\nAn intance of MockedClassService will create for us a mockFactory.\n\nMockedClassService accepts only one variable `SpyFactory` which is a method that returns a spy, the implemantion is very specific to the framework your are using.\n\nThis is an example with using chai spies module\n```js\nconst chai = require('chai');\nchai.use(require('chai-spies'));\nconst mockService = new MockedClassService(chai.spy);\n```\nIt is not required to initialize this service more than once.\nHowever, it's not harmful - so do what's more comfortable for you.\n\n#### mock()\nThis is the only method you should use on `MockedClassService`.\nIt returns a `MockedClassFactory`.\n```js\n    const mockFactory = mockService.mock(/*..Class you will be testing..*/);\n```\nYou should call it once per class.\nHowever, it's not harmful to call it more - so do what's more comfortable for you.\n\n### MockedClassFactory\nAn instance of MockedClassFactory wraps you original class and will be able to create mocked instance per call.\n\nYou should not initilize it your self - you should be geting this from the mock service by calling `mockSerive.mock(/*..Class you will be testing..*/)`.\n\n#### test()\nThis is a chinable method that returns a new MockedClassFactory instance.\n\nThis method accepts both single string and array with multiple strings, these are the keys of the class you are planing to test!\nMeaning that these keys are the only ones that won't be mocked.\n\nThis method only creates a new MockedClassFactory instance, pre-configured with the new configuration.\n\n```js\nmockFactory.test('constructor');\n//Or...\nmockFactory.test(['constructor' , 'method' , 'property']);\n```\n\nImportant!\nWhen your class extends a different parent class.\nYour mocked class will extend a mocked class.\nMeanining the original constructor will not run, instead a 'SuperSpy' consturctor will run.\n\nThis will help you to make sure you class is calling the super consturctor as it should - again something that will require a lot of boilerplate code with this module.\n\n```js\nclass Parent{\n        constructor(i){\n           /*\n            When testing class Child in some cases you don't want to \n            start the construction of the Parent class.\n            Warning! \n            You should remeber to do also integration tests.\n            With these you must test the full integration of Child and Parent.\n           */\n        }\n    }\n    class Child extends Parent{\n        constructor(i){\n            super(i+1);\n        }\n    }\n    const mockService = new MockedClassService(chai.spy);\n    const mockFactory = mockService.mock(Child);\n    const mockView = mockFactory.test('constructor').create(1);\n    expect(mockView.spies.super).to.have.been.called.with(2);\n```\n#### spies()\nThis is a chinable method that returns a new MockedClassFactory instance.\nWith a new instance created the module will put spies instead of the original methods and properties.\nSometimes you will need to create a custome spy, this method let's you setup these special spies.\n\nThis method accepts a single object.\nThe keys are the method/property names you want to be setup with the sepcial spy.\nThe values are these special spies.\n\nThis method only creates a new MockedClassFactory instance, pre-configured with the new configuration.\n\n```js\nmockFactory.spies({\n    method : chai.spy(()=\u003e\"from test\"),\n    property : chai.spy(()=\u003e\"from test\")\n});\n```\n\n#### create()\nThis method creates a new mockView instance.\nOn it you will find your mocked instance and spies as key/value.\n\nIt accepts any arugments and will forward these to your class costructor as is.\n```js\nconst mockView = mockFactory.test('method').create(1,2);\nconst myInstance = mockView.instance;\nconst spies = mockView.spies;\n\nmyInstance.method();\n\n//If it's a function you can access it's spy like this\nexpect(myInstance.otherMethod).to.have.been.called();\n//Or...\nexpect(spies.otherMethod_function).to.have.been.called();\n\n/*If it's a property will replace it with a getter/setter\n* Because of the nature of getter/setter you will only\n* be able to use it from the spies object\n*/\nexpect(spies.name_get).to.have.been.called.with('some_value');\n\n```\nFew things you should know\n  * spies object as a strict covention:\n     * functions will be defined as `${name}_function`.\n     * value will be switched to getter/setter.\n     * getters/setters will be defined as `${name}_get` and `${name}_set`.\n\n\n##Examples\n### Test a mothod without constructor\nA common mistake when trying to tese method `method` of the following class.\n```js\nclass TestMe extends Something{\n    constructor(options){\n        super(options);\n        this.somethingVeryRelatedToOptions(options);\n        this.method(options.ping);\n    }\n    somethingVeryRelatedToOptions(options){\n        /..Some code../\n    }\n    method(ping){\n        if (ping == 'ping')\n            this.pong = 'pong';\n    }\n}\n```\nThe mistake would be to\n```js\nconst testMe = new TestMe({/*..A lot of config..*/});\ntestMe.method('ping');\nexpect(testMe.pong).to.equal('pong');\n```\nNotice the when testing `method`:\n  * we have also tested `somethingVeryRelatedToOptions` logic.\n  * The `Something` class constructor and who knows we have got there...\n\nThis mistake will make the unit tests very breakable as a change in one of `Something` or `somethingVeryRelatedToOptions` will make all our tests fail.\n\nThere are a lot of approaches to this correctly, while I have nothing against, these, these just take way more time mocking the required methods and greater developer proficiency.\n\nThis module will make it much easier for you\n```js\nconst mockService = new MockedClassService(chai.spy);// A service could be defined once...\nconst mockFactory = mockService.mock(TestMe);// A factory could be defined once per class\nconst testMe = mockView.instance;\ntestMe.method('ping');\nexpect(testMe.pong).to.equal('pong')\n```\nAnd a full test case example with Chai and chai spies\n```js\nconst mockService = new MockedClassService(chai.spy); // A service could be defined once...\n\ndescribe('TestMe - example with chai' , ()=\u003e{\n    let mockFactory = mockService.mock(TestMe); // A factory could be defined once per class\n    describe('#constructor' , ()=\u003e{\n        const options = {};\n        const mock = mockFactory.test('constructor').create(options);\n        expect(\n            mock.spies.somethingVeryRelatedToOptions_function\n        ).to.have.been.called.with(options)\n    })\n    describe('#method' , ()=\u003e{\n        let mockView;\n        let testMe;\n        beforeEach(()=\u003e{\n            mockView = mockFactory.test('method').create({});\n            testMe = mockView.instance;\n        })\n        it('Should set `pong` if given `ping`' , ()=\u003e{\n            testMe.method('ping');\n            expect(testMe.pong).to.equal('pong')\n        })\n        it('Shouldn\\'t set `pong` if not given `ping`' , ()=\u003e{\n            testMe.method('dsfsdf');\n            expect(testMe.pong).to.not.equal('pong')\n        })   \n    });\n})\n```\n### Better support for TypeScript projects\n```ts\nconst mockService = new MockedClassService(chai.spy);// A service could be defined once...\nconst mockFactory = mockService.mock\u003cTestMe\u003e(TestMe);// A factory could be defined once per class\nconst testMe = mockView.instance;\n//Notice that both `pong` property and method `method` are recognized by TypeScript\ntestMe.method('ping');\nexpect(testMe.pong).to.equal('pong')\n```\n\n## License\n\n  [MIT](LICENSE)\n\n[npm-image]: https://img.shields.io/npm/v/unit-test-class.svg\n[npm-url]: https://npmjs.org/package/unit-test-class\n[travis-image]: https://img.shields.io/travis/hisco/unit-test-class/master.svg?style=flat-square\n[travis-url]: https://travis-ci.org/hisco/unit-test-class\n[coveralls-image]: https://coveralls.io/repos/github/hisco/unit-test-class/badge.svg?branch=master\n[coveralls-url]: https://coveralls.io/github/hisco/unit-test-class?branch=master\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhisco%2Funit-test-class","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhisco%2Funit-test-class","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhisco%2Funit-test-class/lists"}