├── .circleci └── config.yml ├── .gitignore ├── .npmrc ├── .nvmrc ├── .prettierrc ├── BACKLOG.md ├── LICENSE ├── README.md ├── index.js ├── package-lock.json ├── package.json ├── src ├── index.js ├── operations.js ├── react-component │ ├── Button.js │ ├── __mocks__ │ │ └── httpService.js │ └── httpService.js └── utils.js └── tests ├── __snapshots__ └── snapshots.test.js.snap ├── poke.into.prototype.test.js ├── poke.into.react.components.test.js ├── react-components-async.test.js ├── setupTests.js ├── snapshots.test.js ├── spies.basics.test.js ├── spies.checking-arguments.test.js ├── spies.custom-behavior.test.js ├── spies.spy-on-object-method.test.js ├── spies.times.test.js └── timers.test.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | orbs: 3 | node: circleci/node@5.0.2 4 | 5 | jobs: 6 | build_and_test: 7 | executor: node/default 8 | steps: 9 | - checkout 10 | - node/install-packages: 11 | pkg-manager: npm 12 | - run: 13 | command: npm test 14 | name: Run tests 15 | 16 | workflows: 17 | test_my_app: 18 | jobs: 19 | - build_and_test 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org 2 | 3 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/* -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "semi": true, 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /BACKLOG.md: -------------------------------------------------------------------------------- 1 | # Backlog 2 | 3 | ## [Sinon 4.1.2](http://sinonjs.org/releases/v4.1.2/) 4 | 5 | 6 | ### Sinon Spies 7 | 1. Spy on function example? 8 | 2. Calls: firstCall / secondCall / thirdCall / lastCall 9 | 3. Called 10 | 1. calledBefore / calledAfter / calledImmediatelyBefore / calledImmediatelyAfter 11 | 2. calledOn / alwaysCalledOn / calledWith / alwaysCalledWith / calledWithExactly / alwaysCalledWithExactly 12 | 3. calledWithMatch / alwaysCalledWithMatch 13 | 4. calledWithNew / neverCalledWith / neverCalledWithMatch 14 | 4. threw / alwaysThrew 15 | 5. returned / alwaysReturned 16 | 6. getCall(num) / getCalls 17 | 7. thisValues / exceptions / returnValues 18 | 8. printf 19 | 20 | ### Sinon Stubs 21 | 1. createStubInstance 22 | 2. onFirstCall / onSecondCall / onThirdCall 23 | 3. reset / resetHistory / resetBehavior 24 | 4. returnsArg / returnsThis 25 | 5. resolves / throws / rejects 26 | 6. callsArg / callsArgOn / callsArgWith / callsArgOnWith 27 | 7. callThrough 28 | 8. usingPromise(promiseLibrary) 29 | 9. yields / yieldsOn / yieldsTo / yieldsToOn 30 | 10. callArg / callArgWith 31 | 11. callsArgAsync / callsArgOnAsync / callsArgWithAsync / callsArgOnWithAsync 32 | 12. yieldsAsync / yieldsOnAsync / yieldsToAsync / yieldsToOnAsync 33 | 13. addBehavior 34 | 14. get / set / value 35 | 36 | ### Misc 37 | 1. **Mocks** (and mock expectations) are fake methods (like spies) with pre-programmed behavior (like stubs) 38 | as well as pre-programmed expectations. 39 | 2. **Fake timers**. 40 | 3. **Fake XHR and server**. 41 | 42 | 43 | ## [Jest](http://facebook.github.io/jest/docs/en/api.html) 44 | 45 | ### Mock Functions 46 | 1. getMockName 47 | 2. mock.instances 48 | 3. mockClear / mockName / mockReturnThis 49 | 4. Timer mocks 50 | 51 | ### Misc 52 | 1. Globals, Expect and Matchers examples. 53 | 2. The Jest Object 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Mauro Carrero 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sinon # Jest (a cheatsheet). 2 | 3 | [![tested with jest](https://img.shields.io/badge/tested_with-jest-99424f.svg)](https://github.com/facebook/jest) 4 | [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) 5 | [![CircleCI](https://dl.circleci.com/status-badge/img/circleci/KGvHWvXnzGS4FPUQXWKdAZ/SfzZ9zFtmW67xuFET6yjFZ/tree/master.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/circleci/KGvHWvXnzGS4FPUQXWKdAZ/SfzZ9zFtmW67xuFET6yjFZ/tree/master) 6 | 7 | Some examples on how to achieve the same goal with either of both libraries: [sinon](http://sinonjs.org/) and [jest](http://facebook.github.io/jest/). 8 | Also some of those goals achievable only by one of these tools. 9 | 10 | What's inside? just this **README** file and many **unit tests** using jest as runner. 11 | 12 | ###### Clone the repo: 13 | 14 | ```shell 15 | git clone https://github.com/maurocarrero/sinon-jest-cheatsheet.git 16 | ``` 17 | 18 | ###### Install: 19 | 20 | ```shell 21 | npm install 22 | ``` 23 | 24 | ###### Run tests: 25 | 26 | ```shell 27 | npm test 28 | ``` 29 | 30 | or use watch 31 | 32 | ```shell 33 | npm run test:watch 34 | ``` 35 | 36 | ## Table of Contents 37 | 38 | 1. [Create Spies](#create-spies) 39 | 2. [Are they called?](#are-they-called) 40 | 3. [How many times?](#how-many-times) 41 | 4. [Checking arguments](#checking-arguments) 42 | 5. [Spy on objects methods](#spy-on-objects-method) 43 | 6. [Reset and Restore original method](#restore-original-method) 44 | 7. [Return value](#return-value) 45 | 8. [Custom implementation](#custom-implementation) 46 | 9. [Poking into React component methods](#react-component-methods) 47 | 10. [Timers](#timers) 48 | 49 | ##### [Jest specific](#jest-specific) 50 | 51 | 1. [Snapshot testing](#snapshot-testing) 52 | 2. [Automock](#automock) 53 | 54 | 55 | 56 | ## Spies 57 | 58 | While **sinon** uses three different terms for its snooping functions: `spy`, `stub` and `mock`, 59 | **jest** uses mostly the term `mock function` for what'd be a spy/stub and `manual mock` or `mock` ...well, for mocks. 60 | 61 | ### 1. Create spies: 62 | 63 | ###### sinon 64 | 65 | ```js 66 | const spy = sinon.spy(); 67 | ``` 68 | 69 | ###### jest 70 | 71 | ```js 72 | const spy = jest.fn(); 73 | ``` 74 | 75 | 76 | 77 | ### 2. Know if they are called: 78 | 79 | ###### sinon 80 | 81 | ```js 82 | spy.called; // boolean 83 | spy.notCalled; // boolean 84 | ``` 85 | 86 | ###### jest 87 | 88 | ```js 89 | spy.mock.calls.length; // number; 90 | ``` 91 | 92 | ```js 93 | expect(spy).toHaveBeenCalled(); 94 | ``` 95 | 96 | 97 | 98 | ### 3. How many times are called: 99 | 100 | ###### sinon 101 | 102 | ```js 103 | spy.calledOnce; // boolean 104 | spy.calledTwice; // boolean 105 | spy.calledThrice; // boolean 106 | spy.callCount; // number 107 | ``` 108 | 109 | ###### jest 110 | 111 | ```js 112 | spy.mock.calls.length; // number; 113 | ``` 114 | 115 | ```js 116 | expect(spy).toHaveBeenCalledTimes(n); 117 | ``` 118 | 119 | 120 | 121 | ### 4. Checking arguments: 122 | 123 | ###### sinon 124 | 125 | ```js 126 | // args[call][argIdx] 127 | spy.args[0][0]; 128 | ``` 129 | 130 | ```jsjs 131 | // spy.calledWith(...args) 132 | spy.calledWith(1, 'Hey') 133 | ``` 134 | 135 | ###### jest 136 | 137 | ```js 138 | // mock.calls[call][argIdx] 139 | spy.mock.calls[0][0]; 140 | ``` 141 | 142 | ```js 143 | expect(spy).toHaveBeenCalledWith(1, 'Hey'); 144 | expect(spy).toHaveBeenLastCalledWith(1, 'Hey'); 145 | ``` 146 | 147 | ```js 148 | .toHaveBeenCalledWith(expect.anything()); 149 | .toHaveBeenCalledWith(expect.any(constructor)); 150 | .toHaveBeenCalledWith(expect.arrayContaining([ values ])); 151 | .toHaveBeenCalledWith(expect.objectContaining({ props })); 152 | .toHaveBeenCalledWith(expect.stringContaining(string)); 153 | .toHaveBeenCalledWith(expect.stringMatching(regexp)); 154 | ``` 155 | 156 | 157 | 158 | ### 5. Spy on objects' methods 159 | 160 | ###### sinon 161 | 162 | ```js 163 | sinon.spy(someObject, 'aMethod'); 164 | ``` 165 | 166 | ###### jest 167 | 168 | ```js 169 | jest.spyOn(someObject, 'aMethod'); 170 | ``` 171 | 172 | 173 | 174 | ### 6. Reset and Restore original method 175 | 176 | ###### sinon 177 | 178 | reset both, history and behavior: 179 | 180 | ```js 181 | stub.resetHistory(); 182 | ``` 183 | 184 | reset call history: 185 | 186 | ```js 187 | stub.resetHistory(); 188 | ``` 189 | 190 | reset behaviour: 191 | 192 | ```js 193 | stub.resetBehavior(); 194 | ``` 195 | 196 | restore (remove mock): 197 | 198 | ```js 199 | someObject.aMethod.restore(); 200 | ``` 201 | 202 | ###### jest 203 | 204 | ```js 205 | someObject.aMethod.mockRestore(); 206 | ``` 207 | 208 | 209 | 210 | ### 7. Spy on method and return value: 211 | 212 | ###### sinon 213 | 214 | ```js 215 | stub = sinon.stub(operations, 'add'); 216 | stub.returns(89); 217 | ``` 218 | 219 | ```js 220 | stub.withArgs(42).returns(89); 221 | stub.withArgs(4, 9, 32).returns('OK'); 222 | ``` 223 | 224 | On different calls: 225 | 226 | ```js 227 | stub.onCall(1).returns(7); 228 | expect(fn()).not.toEqual(7); 229 | expect(fn()).toEqual(7); 230 | ``` 231 | 232 | ###### jest 233 | 234 | ```js 235 | jest.spyOn(operations, 'add').mockReturnValue(89); 236 | ``` 237 | 238 | On different calls: 239 | 240 | ```js 241 | spy.mockReturnValueOnce(undefined); 242 | spy.mockReturnValueOnce(7); 243 | 244 | expect(fn()).not.toEqual(7); 245 | expect(fn()).toEqual(7); 246 | ``` 247 | 248 | 249 | 250 | ### 8. Custom implementation: 251 | 252 | ###### sinon 253 | 254 | ```js 255 | sinonStub.callsFake(function() { 256 | return 'Peteco'; 257 | }); 258 | expect(operations.add(1, 2)).toEqual('Peteco'); 259 | ``` 260 | 261 | Different implementation on different call: 262 | 263 | ###### jest 264 | 265 | ```js 266 | jest.spyOn(operations, 'add').mockImplementation(function(a, b, c) { 267 | if (a === 42) { 268 | return 89; 269 | } 270 | if (a === 4 && b === 9 && c === 32) { 271 | return 'OK'; 272 | } 273 | }); 274 | ``` 275 | 276 | 277 | 278 | ### 9. Poking into React components methods: 279 | 280 | Suppose `foo` is called when mounting Button. 281 | 282 | ###### sinon 283 | 284 | ```js 285 | sinon.spy(Button.prototype, 'foo'); 286 | 287 | wrapper = shallow( 9 | `; 10 | 11 | exports[`should match a function 1`] = `undefined`; 12 | -------------------------------------------------------------------------------- /tests/poke.into.prototype.test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon'); 2 | 3 | function Peteco() {} 4 | 5 | Peteco.prototype.hola = function() { 6 | return 'hola'; 7 | }; 8 | Peteco.prototype.chau = function() { 9 | return 'chau'; 10 | }; 11 | 12 | it("sinon.spy(Peteco.prototype, 'hola')", function() { 13 | const holaSpy = sinon.spy(Peteco.prototype, 'hola'); 14 | const chauSpy = sinon.spy(Peteco.prototype, 'chau'); 15 | 16 | new Peteco().hola(); 17 | new Peteco().chau(); 18 | 19 | expect(holaSpy.called).toEqual(true); 20 | expect(chauSpy.called).toEqual(true); 21 | }); 22 | 23 | it("jest.spyOn(Peteco.prototype, 'hola')", function() { 24 | const holaSpy = jest.spyOn(Peteco.prototype, 'hola'); 25 | const chauSpy = jest.spyOn(Peteco.prototype, 'chau'); 26 | 27 | new Peteco().hola(); 28 | new Peteco().chau(); 29 | 30 | expect(holaSpy).toHaveBeenCalled(); 31 | expect(chauSpy).toHaveBeenCalled(); 32 | }); 33 | -------------------------------------------------------------------------------- /tests/poke.into.react.components.test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon'); 2 | 3 | const React = require('react'); 4 | const { shallow } = require('enzyme'); 5 | 6 | const Button = require('../src/react-component/Button'); 7 | 8 | let wrapper; 9 | let instance; 10 | 11 | describe('sinon', function() { 12 | let sinonCDMSpy; 13 | let sinonRenderSpy; 14 | let sinonDoSomethingSpy; 15 | let sinonClickHandlerSpy; 16 | 17 | beforeEach(function() { 18 | sinonCDMSpy = sinon.spy(Button.prototype, 'componentDidMount'); 19 | sinonRenderSpy = sinon.spy(Button.prototype, 'render'); 20 | sinonDoSomethingSpy = sinon.spy(Button.prototype, 'doSomething'); 21 | sinonClickHandlerSpy = sinon.spy(Button.prototype, 'clickHandler'); 22 | 23 | wrapper = shallow(React.createElement(Button)); 24 | }); 25 | 26 | afterEach(function() { 27 | sinonCDMSpy.restore(); 28 | sinonRenderSpy.restore(); 29 | sinonDoSomethingSpy.restore(); 30 | sinonClickHandlerSpy.restore(); 31 | }); 32 | 33 | it('spy on componentDidMount', function() { 34 | expect(sinonCDMSpy.called).toEqual(true); 35 | }); 36 | 37 | it('spy on render', function() { 38 | expect(sinonRenderSpy.called).toEqual(true); 39 | }); 40 | 41 | it('spy on doSomething', function() { 42 | expect(sinonDoSomethingSpy.called).toEqual(true); 43 | }); 44 | 45 | it('spy on clickHandler', function() { 46 | expect(sinonClickHandlerSpy.called).toEqual(false); 47 | wrapper.simulate('click'); 48 | expect(sinonClickHandlerSpy.called).toEqual(true); 49 | }); 50 | }); 51 | 52 | describe('jest', function() { 53 | let jestCDMSpy; 54 | let jestRenderSpy; 55 | let jestDoSomethingSpy; 56 | let jestClickHandlerSpy; 57 | 58 | beforeEach(function() { 59 | jestCDMSpy = jest.spyOn(Button.prototype, 'componentDidMount'); 60 | jestRenderSpy = jest.spyOn(Button.prototype, 'render'); 61 | jestDoSomethingSpy = jest.spyOn(Button.prototype, 'doSomething'); 62 | jestClickHandlerSpy = jest.spyOn(Button.prototype, 'clickHandler'); 63 | 64 | wrapper = shallow(React.createElement(Button)); 65 | }); 66 | 67 | afterEach(function() { 68 | jestCDMSpy.mockRestore(); 69 | jestRenderSpy.mockRestore(); 70 | jestDoSomethingSpy.mockRestore(); 71 | jestClickHandlerSpy.mockRestore(); 72 | }); 73 | 74 | it('spy on componentDidMount', function() { 75 | expect(jestCDMSpy).toHaveBeenCalled(); 76 | }); 77 | 78 | it('spy on doSomething', function() { 79 | expect(jestDoSomethingSpy).toHaveBeenCalled(); 80 | }); 81 | 82 | it('spy on clickHandler', function() { 83 | wrapper.simulate('click'); 84 | 85 | expect(jestClickHandlerSpy).toHaveBeenCalled(); 86 | }); 87 | }); 88 | 89 | describe('sinon && jest', function() { 90 | it('spying both at a time', function() { 91 | const jestSpy = jest.spyOn(Button.prototype, 'doSomething'); 92 | const sinonSpy = sinon.spy(Button.prototype, 'doSomething'); 93 | 94 | expect(jestSpy).not.toHaveBeenCalled(); 95 | expect(sinonSpy.called).toEqual(false); 96 | 97 | wrapper = shallow(React.createElement(Button)); 98 | 99 | expect(jestSpy).toHaveBeenCalled(); 100 | expect(sinonSpy.called).toEqual(true); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /tests/react-components-async.test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon'); 2 | 3 | const React = require('react'); 4 | const { shallow } = require('enzyme'); 5 | 6 | const Button = require('../src/react-component/Button'); 7 | 8 | let httpServiceMock = require('../src/react-component/httpService'); 9 | 10 | const EXPECTED = 'url was requested: https://api.github.com/'; 11 | const INITIAL = 'initial doSomething'; 12 | 13 | let wrapper; 14 | 15 | describe('sinon', function() { 16 | it('should call the mocked service', function(done) { 17 | const stub = sinon.stub(Button.prototype, 'doSomething').callThrough(); 18 | 19 | wrapper = shallow(React.createElement(Button)); 20 | 21 | expect(wrapper.state('task')).toEqual(INITIAL); 22 | 23 | wrapper.simulate('click'); 24 | wrapper.simulate('click'); 25 | 26 | process.nextTick(() => { 27 | try { 28 | // Button.prototype.doSomething spy 29 | expect(stub.callCount).toEqual(3); 30 | expect(stub.args[0][0]).toEqual(INITIAL); 31 | expect(stub.args[1][0]).toEqual(EXPECTED); 32 | expect(stub.args[2][0]).toEqual(EXPECTED); 33 | 34 | // The state of the component has been updated 35 | expect(wrapper.state('task')).toEqual(EXPECTED); 36 | done(); 37 | } catch (e) { 38 | return done(e); 39 | } 40 | }); 41 | 42 | expect(httpServiceMock.get.callCount).toEqual(2); 43 | 44 | stub.restore(); 45 | }); 46 | }); 47 | 48 | describe('jest', function() { 49 | it('should call the mocked service', function(done) { 50 | const jestSpy = jest.spyOn(Button.prototype, 'doSomething'); 51 | 52 | // httpServiceMock is still a sinon stub since it is implemented in 53 | // the manual mock. 54 | // We use .resetHistory instead of .reset, otherwise behavior is also reset. 55 | httpServiceMock.get.resetHistory(); 56 | 57 | wrapper = shallow(React.createElement(Button)); 58 | 59 | console.log('POST', jestSpy.mock.calls[0][0]); 60 | 61 | expect(wrapper.state('task')).toEqual(INITIAL); 62 | 63 | wrapper.simulate('click'); 64 | wrapper.simulate('click'); 65 | 66 | process.nextTick(() => { 67 | try { 68 | // Button.prototype.doSomething spy 69 | expect(jestSpy.mock.calls.length).toEqual(3); 70 | expect(jestSpy.mock.calls[0][0]).toEqual(INITIAL); 71 | 72 | // The state of the component has been updated 73 | expect(wrapper.state('task')).toEqual(EXPECTED); 74 | jestSpy.mockRestore(); 75 | done(); 76 | } catch (e) { 77 | jestSpy.mockRestore(); 78 | return done(e); 79 | } 80 | }); 81 | 82 | expect(httpServiceMock.get.callCount).toEqual(2); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /tests/setupTests.js: -------------------------------------------------------------------------------- 1 | global.requestAnimationFrame = function(callback) { 2 | setTimeout(callback, 0); 3 | }; 4 | 5 | const Enzyme = require('enzyme'); 6 | const Adapter = require('enzyme-adapter-react-16'); 7 | 8 | Enzyme.configure({ adapter: new Adapter() }); 9 | 10 | // Enabling automock since now is disabled by default: 11 | // https://facebook.github.io/jest/blog/2016/09/01/jest-15.html#disabled-automocking 12 | jest.enableAutomock(); 13 | -------------------------------------------------------------------------------- /tests/snapshots.test.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | const ReactTestRenderer = require('react-test-renderer'); 3 | 4 | const operations = require('../src/index'); 5 | const Button = require('../src/react-component/Button'); 6 | 7 | it('should match a function', function() { 8 | expect(operations.add(45, 234)).toMatchSnapshot(); 9 | }); 10 | 11 | it('should match a React Component', function() { 12 | expect(ReactTestRenderer.create(React.createElement(Button))).toMatchSnapshot(); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/spies.basics.test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon'); 2 | 3 | let sinonSpy; 4 | let jestSpy; 5 | 6 | beforeAll(function() { 7 | sinonSpy = sinon.spy(); 8 | jestSpy = jest.fn(); 9 | }); 10 | 11 | beforeEach(function() { 12 | sinonSpy.resetHistory(); 13 | jestSpy.mockReset(); 14 | }); 15 | 16 | afterAll(function() { 17 | sinonSpy = null; 18 | jestSpy = null; 19 | }); 20 | 21 | describe('called | not called', function() { 22 | describe('sinon: sinon.spy', function() { 23 | it('spy.called', function() { 24 | expect(sinonSpy.called).toEqual(false); 25 | 26 | sinonSpy(); 27 | 28 | expect(sinonSpy.called).toEqual(true); 29 | }); 30 | 31 | it('spy.notCalled', function() { 32 | expect(sinonSpy.notCalled).toEqual(true); 33 | 34 | sinonSpy(); 35 | 36 | expect(sinonSpy.notCalled).toEqual(false); 37 | }); 38 | }); 39 | 40 | describe('jest: jest.fn', function() { 41 | it('.mock.calls.length', function() { 42 | jestSpy(); 43 | 44 | expect(jestSpy.mock.calls.length).toEqual(1); 45 | }); 46 | 47 | it('expect(spy).toHaveBeenCalled', function() { 48 | jestSpy(); 49 | 50 | expect(jestSpy).toHaveBeenCalled(); 51 | }); 52 | 53 | it('expect(spy).toBeCalled', function() { 54 | jestSpy(); 55 | 56 | expect(jestSpy).toBeCalled(); 57 | }); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /tests/spies.checking-arguments.test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon'); 2 | 3 | let sinonSpy; 4 | let jestSpy; 5 | 6 | beforeAll(function() { 7 | sinonSpy = sinon.spy(); 8 | jestSpy = jest.fn(); 9 | }); 10 | 11 | beforeEach(function() { 12 | sinonSpy.resetHistory(); 13 | jestSpy.mockReset(); 14 | }); 15 | 16 | afterAll(function() { 17 | sinonSpy = null; 18 | jestSpy = null; 19 | }); 20 | 21 | describe('checking arguments', function() { 22 | /** 23 | * sinon 24 | */ 25 | describe('sinon', function() { 26 | it('spy.args', function() { 27 | sinonSpy(1, 2); 28 | 29 | expect(sinonSpy.args[0][0]).toEqual(1); 30 | expect(sinonSpy.args[0][1]).toEqual(2); 31 | }); 32 | 33 | it('spy.calledWith', function() { 34 | sinonSpy(1, 2); 35 | 36 | expect(sinonSpy.calledWith(1, 2)).toEqual(true); 37 | }); 38 | }); 39 | 40 | describe('jest', function() { 41 | it('spy.mock.calls', function() { 42 | jestSpy(1, 2); 43 | 44 | expect(jestSpy.mock.calls[0][0]).toEqual(1); 45 | expect(jestSpy.mock.calls[0][1]).toEqual(2); 46 | }); 47 | 48 | it('.toHaveBeenCalledWith', function() { 49 | jestSpy(1, 2); 50 | 51 | expect(jestSpy).toHaveBeenCalledWith(1, 2); 52 | }); 53 | }); 54 | 55 | it('expect .toHaveBeenLastCalledWith', function() { 56 | jestSpy(); 57 | jestSpy(); 58 | jestSpy(); 59 | jestSpy(7); 60 | 61 | expect(jestSpy).toHaveBeenLastCalledWith(7); 62 | }); 63 | 64 | describe('expect .toHaveBeenCalledWith', function() { 65 | it('expect.anything()', function() { 66 | jestSpy(7); 67 | 68 | expect(jestSpy).toHaveBeenLastCalledWith(expect.anything()); 69 | }); 70 | 71 | it('expect.any(constructor)', function() { 72 | jestSpy(7); 73 | 74 | expect(jestSpy).toHaveBeenLastCalledWith(expect.any(Number)); 75 | }); 76 | 77 | it('expect.arrayContaining([ values ])', function() { 78 | jestSpy([7, 27, 33, 48]); 79 | 80 | expect(jestSpy).toHaveBeenLastCalledWith(expect.arrayContaining([33, 48])); 81 | }); 82 | 83 | it('expect.objectContaining({ props })', function() { 84 | const mock = { 85 | id: 1, 86 | name: 'Peteco' 87 | }; 88 | jestSpy(mock); 89 | 90 | expect(jestSpy).toHaveBeenCalledWith(expect.objectContaining({ id: mock.id })); 91 | expect(jestSpy).toHaveBeenLastCalledWith(expect.objectContaining({ name: mock.name })); 92 | }); 93 | 94 | it('expect.stringContaining(string)', function() { 95 | jestSpy('This was all about foo bar.'); 96 | 97 | expect(jestSpy).toHaveBeenCalledWith(expect.stringContaining('foo')); 98 | }); 99 | 100 | it('expect.stringMatching(regexp)', function() { 101 | jestSpy('This was all about foo bar.'); 102 | 103 | expect(jestSpy).toHaveBeenCalledWith(expect.stringMatching(/^This/)); 104 | }); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /tests/spies.custom-behavior.test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon'); 2 | 3 | // Unmocking operations dependency 4 | jest.unmock('../src/operations'); 5 | const operations = require('../src/operations'); 6 | 7 | let sinonStub; 8 | let jestSpy; 9 | 10 | beforeAll(function() { 11 | sinonStub = sinon.spy(); 12 | jestSpy = jest.fn(); 13 | }); 14 | 15 | afterAll(function() { 16 | sinonStub = null; 17 | jestSpy = null; 18 | }); 19 | 20 | describe("sinon's stub and jest's spyOn", function() { 21 | describe('sinon.stub', function() { 22 | beforeEach(() => { 23 | sinonStub = sinon.stub(operations, 'add'); 24 | }); 25 | 26 | afterEach(() => { 27 | sinonStub.restore(); 28 | }); 29 | 30 | it("sinon.stub(obj, 'method')", function() { 31 | operations.add(1, 2); 32 | 33 | expect(sinonStub.calledOnce).toEqual(true); 34 | }); 35 | 36 | it('.returns', function() { 37 | sinonStub.returns(89); 38 | 39 | const result = operations.add(1, 2); 40 | 41 | expect(sinonStub.calledOnce).toEqual(true); 42 | expect(result).toEqual(89); 43 | }); 44 | 45 | it('.withArgs.returns', function() { 46 | sinonStub.withArgs(42).returns(89); 47 | sinonStub.withArgs(4, 9, 32).returns('OK'); 48 | 49 | const noReturn = operations.add(1, 2); 50 | const result = operations.add(42); 51 | const result2 = operations.add(4, 9, 32); 52 | 53 | expect(noReturn).toEqual(undefined); 54 | expect(result).toEqual(89); 55 | expect(result2).toEqual('OK'); 56 | }); 57 | }); 58 | 59 | describe('jest', () => { 60 | beforeEach(() => { 61 | jestSpy = jest.spyOn(operations, 'add'); 62 | }); 63 | 64 | afterEach(() => { 65 | jestSpy.mockRestore(); 66 | }); 67 | 68 | it("jest.spyOn(obj, 'method')", function() { 69 | operations.add(3, 4); 70 | 71 | expect(jestSpy).toHaveBeenCalledTimes(1); 72 | }); 73 | 74 | it('.mockReturnValue', function() { 75 | jestSpy.mockReturnValue(89); 76 | 77 | const result = operations.add(3, 4); 78 | 79 | expect(jestSpy).toHaveBeenCalled(); 80 | expect(result).toEqual(89); 81 | }); 82 | 83 | it('.mockReturnValueOnce', function() { 84 | jestSpy.mockReturnValueOnce(89); 85 | 86 | let result = operations.add(3, 4); 87 | 88 | expect(jestSpy).toHaveBeenCalled(); 89 | expect(result).toEqual(89); 90 | 91 | result = operations.add(3, 4); 92 | 93 | expect(result).toEqual(7); 94 | }); 95 | }); 96 | 97 | describe('mocking implementation', function() { 98 | describe('sinon', function() { 99 | beforeEach(() => { 100 | sinonStub = sinon.stub(operations, 'add'); 101 | }); 102 | 103 | afterEach(() => { 104 | sinonStub.restore(); 105 | }); 106 | 107 | it('.callsFake', function() { 108 | sinonStub.callsFake(function() { 109 | return 'Peteco'; 110 | }); 111 | expect(operations.add(1, 2)).toEqual('Peteco'); 112 | }); 113 | }); 114 | 115 | describe('jest', () => { 116 | beforeEach(() => { 117 | jestSpy = jest.spyOn(operations, 'add'); 118 | }); 119 | 120 | afterEach(() => { 121 | jestSpy.mockRestore(); 122 | }); 123 | 124 | it('.mockImplementation(function () {})', function() { 125 | jestSpy.mockImplementation(function() { 126 | return 89; 127 | }); 128 | 129 | const result = operations.add(3, 4); 130 | 131 | expect(jestSpy).toHaveBeenCalled(); 132 | expect(result).toEqual(89); 133 | }); 134 | 135 | it('.mockImplementation evaluating args', function() { 136 | jestSpy = jest.spyOn(operations, 'add').mockImplementation(function(a, b, c) { 137 | if (a === 42) { 138 | return 89; 139 | } 140 | if (a === 4 && b === 9 && c === 32) { 141 | return 'OK'; 142 | } 143 | }); 144 | 145 | const noReturn = operations.add(1, 2); 146 | const result = operations.add(42); 147 | const result2 = operations.add(4, 9, 32); 148 | 149 | expect(noReturn).toEqual(undefined); 150 | expect(result).toEqual(89); 151 | expect(result2).toEqual('OK'); 152 | 153 | jestSpy.mockRestore(); 154 | }); 155 | }); 156 | }); 157 | 158 | describe('different implementations on different calls', function() { 159 | describe('sinon', function() { 160 | beforeEach(() => { 161 | sinonStub = sinon.stub(operations, 'add'); 162 | sinonStub.onCall(0).returns('Peteco'); 163 | sinonStub.onCall(1).returns('Rambla'); 164 | sinonStub.onCall(2).returns('Palta'); 165 | }); 166 | 167 | afterEach(() => { 168 | sinonStub.restore(); 169 | }); 170 | 171 | it('.onCall', function() { 172 | expect(operations.add()).toEqual('Peteco'); 173 | expect(operations.add()).toEqual('Rambla'); 174 | expect(operations.add()).toEqual('Palta'); 175 | }); 176 | }); 177 | 178 | describe('jest', () => { 179 | beforeEach(() => { 180 | jestSpy = jest.spyOn(operations, 'add'); 181 | }); 182 | 183 | afterEach(() => { 184 | jestSpy.mockRestore(); 185 | }); 186 | 187 | it('.mockImplementationOnce', function() { 188 | jestSpy.mockReturnValueOnce('Peteco'); 189 | jestSpy.mockReturnValueOnce('Rambla'); 190 | jestSpy.mockReturnValueOnce('Palta'); 191 | 192 | expect(operations.add()).toEqual('Peteco'); 193 | expect(operations.add()).toEqual('Rambla'); 194 | expect(operations.add()).toEqual('Palta'); 195 | }); 196 | }); 197 | }); 198 | }); 199 | -------------------------------------------------------------------------------- /tests/spies.spy-on-object-method.test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon'); 2 | 3 | const someObject = { 4 | id: 43, 5 | model: 'C4', 6 | getModel: function() { 7 | return this.model; 8 | } 9 | }; 10 | 11 | let sinonSpy; 12 | let jestSpy; 13 | 14 | beforeAll(function() { 15 | sinonSpy = sinon.spy(); 16 | jestSpy = jest.fn(); 17 | }); 18 | 19 | beforeEach(function() { 20 | sinonSpy.resetHistory(); 21 | jestSpy.mockReset(); 22 | }); 23 | 24 | afterAll(function() { 25 | sinonSpy = null; 26 | jestSpy = null; 27 | }); 28 | 29 | describe('spy on method', function() { 30 | it("sinon.spy(obj, 'method')", function() { 31 | sinonSpy = sinon.spy(someObject, 'getModel'); 32 | 33 | someObject.getModel(); 34 | 35 | expect(sinonSpy.called).toEqual(true); 36 | 37 | sinonSpy.restore(); 38 | }); 39 | 40 | it("jest.spyOn(obj, 'method')", function() { 41 | jestSpy = jest.spyOn(someObject, 'getModel'); 42 | 43 | someObject.getModel(); 44 | 45 | expect(jestSpy).toHaveBeenCalled(); 46 | 47 | jestSpy.mockRestore(); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /tests/spies.times.test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon'); 2 | 3 | let sinonSpy; 4 | let jestSpy; 5 | 6 | beforeAll(function() { 7 | sinonSpy = sinon.spy(); 8 | jestSpy = jest.fn(); 9 | }); 10 | 11 | beforeEach(function() { 12 | sinonSpy.resetHistory(); 13 | jestSpy.mockReset(); 14 | }); 15 | 16 | afterAll(function() { 17 | sinonSpy = null; 18 | jestSpy = null; 19 | }); 20 | 21 | describe('counting times', function() { 22 | /** 23 | * sinon 24 | */ 25 | describe('sinon', function() { 26 | it('spy.calledOnce', function() { 27 | sinonSpy(); 28 | 29 | expect(sinonSpy.calledOnce).toEqual(true); 30 | }); 31 | 32 | it('spy.calledTwice', function() { 33 | sinonSpy(); 34 | sinonSpy(); 35 | 36 | expect(sinonSpy.calledTwice).toEqual(true); 37 | }); 38 | 39 | it('spy.calledThrice', function() { 40 | sinonSpy(); 41 | sinonSpy(); 42 | sinonSpy(); 43 | 44 | expect(sinonSpy.calledThrice).toEqual(true); 45 | }); 46 | 47 | it('spy.callCount', function() { 48 | sinonSpy(); 49 | 50 | expect(sinonSpy.callCount).toEqual(1); 51 | 52 | sinonSpy(); 53 | 54 | expect(sinonSpy.callCount).toEqual(2); 55 | 56 | sinonSpy(); 57 | 58 | expect(sinonSpy.callCount).toEqual(3); 59 | }); 60 | }); 61 | 62 | /** 63 | * jest 64 | */ 65 | describe('jest', function() { 66 | it('spy.mock.calls.length', function() { 67 | jestSpy(); 68 | 69 | expect(jestSpy.mock.calls.length).toEqual(1); 70 | 71 | jestSpy(); 72 | 73 | expect(jestSpy.mock.calls.length).toEqual(2); 74 | }); 75 | 76 | it('expect .toHaveBeenCalled', function() { 77 | jestSpy(); 78 | 79 | expect(jestSpy).toHaveBeenCalled(); 80 | }); 81 | 82 | it('expect .toHaveBeenCalledTimes', function() { 83 | jestSpy(); 84 | 85 | expect(jestSpy).toHaveBeenCalledTimes(1); 86 | 87 | jestSpy(); 88 | 89 | expect(jestSpy).toHaveBeenCalledTimes(2); 90 | }); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /tests/timers.test.js: -------------------------------------------------------------------------------- 1 | const sinon = require('sinon'); 2 | const lolex = require('lolex'); 3 | 4 | const TIMESTAMP = 233550000000; 5 | 6 | const formatDate = d => `${d.getDate()}/${d.getMonth() + 1}/${d.getFullYear()}`; 7 | 8 | const timerFn = cb => setTimeout(cb, 100); 9 | 10 | let clock; 11 | 12 | describe('sinon .useFakeTimers', () => { 13 | it('fake Date', () => { 14 | clock = sinon.useFakeTimers({ 15 | now: new Date(TIMESTAMP) 16 | }); 17 | 18 | expect(formatDate(new Date())).toEqual('27/5/1977'); 19 | 20 | clock.restore(); 21 | }); 22 | 23 | it('fake nextTick', () => { 24 | clock = sinon.useFakeTimers({ 25 | toFake: ['nextTick'] 26 | }); 27 | 28 | let called = false; 29 | 30 | process.nextTick(function() { 31 | called = true; 32 | }); 33 | 34 | clock.runAll(); //forces nextTick calls to flush synchronously 35 | 36 | expect(called).toBeTruthy(); 37 | 38 | clock.restore(); 39 | }); 40 | 41 | it('fake tick', () => { 42 | let result = ''; 43 | 44 | clock = sinon.useFakeTimers(); 45 | 46 | setImmediate(function() { 47 | result = 'tick'; 48 | }); 49 | 50 | setTimeout(function() { 51 | result = 'tock'; 52 | }, 15); 53 | 54 | setTimeout(function() { 55 | result = 'tack'; 56 | }, 35); 57 | 58 | expect(result).toEqual(''); 59 | clock.tick(); 60 | expect(result).toEqual('tick'); 61 | clock.tick(15); 62 | expect(result).toEqual('tock'); 63 | clock.tick(20); 64 | expect(result).toEqual('tack'); 65 | 66 | clock.restore(); 67 | }); 68 | }); 69 | 70 | describe('jest', function() { 71 | describe('.fakeTimers', function() { 72 | const innerSpy = jest.fn(); 73 | const callback = jest.fn().mockImplementation(() => { 74 | return setTimeout(innerSpy, 200); 75 | }); 76 | let timeout; 77 | 78 | beforeAll(() => { 79 | // Enable fake timers 80 | jest.useFakeTimers(); 81 | }); 82 | 83 | beforeEach(() => { 84 | timeout = timerFn(callback); 85 | }); 86 | 87 | afterEach(() => { 88 | callback.mockClear(); 89 | innerSpy.mockClear(); 90 | clearTimeout(timeout); 91 | }); 92 | 93 | afterAll(() => { 94 | jest.clearAllTimers(); 95 | }); 96 | 97 | 98 | it('setTimeout is NOT a mock function as before but it works as expected in Jest 29', function() { 99 | expect(setTimeout.mock).toBeUndefined(); 100 | }); 101 | 102 | it('.runAllTimers', () => { 103 | expect(callback).not.toBeCalled(); 104 | 105 | // Fast-forward until all timers have been executed 106 | jest.runAllTimers(); 107 | 108 | expect(callback.mock.calls.length).toEqual(1); 109 | expect(innerSpy.mock.calls.length).toEqual(1); 110 | }); 111 | 112 | it('.runOnlyPendingTimers', () => { 113 | jest.runOnlyPendingTimers(); 114 | 115 | expect(callback.mock.calls.length).toEqual(1); 116 | expect(innerSpy.mock.calls.length).toEqual(0); 117 | 118 | jest.runOnlyPendingTimers(); 119 | 120 | expect(innerSpy.mock.calls.length).toEqual(1); 121 | }); 122 | 123 | it('.advanceTimersByTime | jest 26.0.0: .advanceTimersByTime', () => { 124 | jest.advanceTimersByTime(100); 125 | 126 | expect(callback.mock.calls.length).toEqual(1); 127 | expect(innerSpy.mock.calls.length).toEqual(0); 128 | 129 | jest.advanceTimersByTime(200); 130 | 131 | expect(innerSpy.mock.calls.length).toEqual(1); 132 | }); 133 | }); 134 | describe('lolex (to provide timer API for Jest)', function() { 135 | it('mock the Date', function() { 136 | clock = lolex.install({ 137 | now: TIMESTAMP 138 | }); 139 | 140 | expect(formatDate(new Date())).toEqual('27/5/1977'); 141 | 142 | clock.uninstall(); 143 | }); 144 | 145 | it('fake nextTick', () => { 146 | clock = lolex.install({ 147 | toFake: ['nextTick'] 148 | }); 149 | 150 | let spy = jest.fn(); 151 | 152 | process.nextTick(spy); 153 | 154 | clock.runAll(); //forces nextTick calls to flush synchronously 155 | 156 | expect(spy).toHaveBeenCalled(); 157 | 158 | clock.uninstall(); 159 | }); 160 | 161 | it('fake tick', () => { 162 | let result = ''; 163 | 164 | clock = lolex.install(); 165 | 166 | setImmediate(function() { 167 | result = 'tick'; 168 | }); 169 | 170 | setTimeout(function() { 171 | result = 'tock'; 172 | }, 15); 173 | 174 | setTimeout(function() { 175 | result = 'tack'; 176 | }, 35); 177 | 178 | expect(result).toEqual(''); 179 | clock.tick(); 180 | expect(result).toEqual('tick'); 181 | clock.tick(15); 182 | expect(result).toEqual('tock'); 183 | clock.tick(20); 184 | expect(result).toEqual('tack'); 185 | 186 | clock.uninstall(); 187 | }); 188 | }); 189 | }); 190 | --------------------------------------------------------------------------------