├── .editorconfig ├── .eslintrc ├── .gitignore ├── .npmrc ├── .prettierrc ├── README.md ├── fixtures ├── random-user-data-async.json └── random-user-data-promise.json ├── helpers └── nock.js ├── package-lock.json ├── package.json └── userQueryHelper ├── defaultUser.js ├── userQueryHelper.js └── userQueryHelper.test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 2 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "skyscanner", 4 | "prettier" 5 | ], 6 | "env": { 7 | "jest": true 8 | }, 9 | "plugins": [ 10 | "prettier" 11 | ], 12 | "rules": { 13 | "prettier/prettier": "error", 14 | "no-undef": "warn", 15 | "no-console": "off", 16 | "import/no-extraneous-dependencies": [0], 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # misc 7 | .DS_Store 8 | npm-debug.log 9 | .vscode 10 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=http://registry.npmjs.org/ 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is an example repo to go along with this ['how to' article](https://codeburst.io/testing-mocking-http-requests-with-nock-480e3f164851). 2 | 3 | It uses [Nock](https://github.com/nock/nock) alongside a [Random User API](https://randomuser.me/documentation). 4 | 5 | #### Linting: 6 | 7 | For linting [eslint-config-skyscanner](https://github.com/Skyscanner/eslint-config-skyscanner ) has been used, along with [prettier](https://github.com/prettier/prettier). This is not included in each directory, if you wish to add it duplicate the [.eslintrc](.eslintrc) and [.prettierrc](.prettierrc) and run: 8 | 9 | ```sh 10 | ( 11 | export PKG=eslint-config-skyscanner; 12 | npm info "$PKG@latest" peerDependencies --json | command sed 's/[\{\},]//g ; s/: /@/g' | xargs npm install --save-dev 13 | "$PKG@latest" 14 | ) 15 | ``` 16 | to setup eslint, and then the following to integrate prettier: 17 | ```sh 18 | npm install -D eslint-config-prettier eslint-plugin-prettier prettier 19 | ``` 20 | -------------------------------------------------------------------------------- /fixtures/random-user-data-async.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "scope": "https://randomuser.me:443", 4 | "method": "GET", 5 | "path": "/api/", 6 | "body": "", 7 | "status": 200, 8 | "response": { 9 | "results": [ 10 | { 11 | "gender": "female", 12 | "name": { 13 | "title": "mrs", 14 | "first": "nanda", 15 | "last": "bogaards" 16 | }, 17 | "location": { 18 | "street": "7182 annastraat", 19 | "city": "heemstede", 20 | "state": "limburg", 21 | "postcode": 21656, 22 | "coordinates": { 23 | "latitude": "39.3347", 24 | "longitude": "127.5639" 25 | }, 26 | "timezone": { 27 | "offset": "-2:00", 28 | "description": "Mid-Atlantic" 29 | } 30 | }, 31 | "email": "nanda.bogaards@example.com", 32 | "login": { 33 | "uuid": "a176b157-2987-4918-b383-1bd45d630a59", 34 | "username": "brownbird758", 35 | "password": "lemons", 36 | "salt": "KGCJeGnp", 37 | "md5": "aa4e41b906472721543dc37b706a7fc3", 38 | "sha1": "f0c0adf8a636ce289b380cf3403157ee63bf09e8", 39 | "sha256": "236986cfd98088c7e0a7f6086f79db42c9dfd12a14f261394a7e0903e4e5f60f" 40 | }, 41 | "dob": { 42 | "date": "1990-02-21T22:07:46Z", 43 | "age": 28 44 | }, 45 | "registered": { 46 | "date": "2006-05-09T10:10:59Z", 47 | "age": 12 48 | }, 49 | "phone": "(831)-063-5835", 50 | "cell": "(219)-420-4330", 51 | "id": { 52 | "name": "BSN", 53 | "value": "98489064" 54 | }, 55 | "picture": { 56 | "large": "https://randomuser.me/api/portraits/women/31.jpg", 57 | "medium": "https://randomuser.me/api/portraits/med/women/31.jpg", 58 | "thumbnail": "https://randomuser.me/api/portraits/thumb/women/31.jpg" 59 | }, 60 | "nat": "NL" 61 | } 62 | ], 63 | "info": { 64 | "seed": "fa0fa5935e849e88", 65 | "results": 1, 66 | "page": 1, 67 | "version": "1.2" 68 | } 69 | }, 70 | "rawHeaders": [ 71 | "Date", 72 | "Sun, 08 Jul 2018 10:10:13 GMT", 73 | "Content-Type", 74 | "application/json; charset=utf-8", 75 | "Transfer-Encoding", 76 | "chunked", 77 | "Connection", 78 | "close", 79 | "Set-Cookie", 80 | "__cfduid=d808998f42088ee35f6b63b6fffa6758f1531044613; expires=Mon, 08-Jul-19 10:10:13 GMT; path=/; domain=.randomuser.me; HttpOnly", 81 | "X-Powered-By", 82 | "Express", 83 | "Access-Control-Allow-Origin", 84 | "*", 85 | "Cache-Control", 86 | "no-cache", 87 | "ETag", 88 | "W/\"44f-TUAlWr2MDYw9raMMFLjE2A\"", 89 | "Vary", 90 | "Accept-Encoding", 91 | "Expect-CT", 92 | "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", 93 | "Server", 94 | "cloudflare" 95 | ] 96 | } 97 | ] -------------------------------------------------------------------------------- /fixtures/random-user-data-promise.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "scope": "https://randomuser.me:443", 4 | "method": "GET", 5 | "path": "/api/", 6 | "body": "", 7 | "status": 200, 8 | "response": { 9 | "results": [ 10 | { 11 | "gender": "male", 12 | "name": { 13 | "title": "mr", 14 | "first": "elvis", 15 | "last": "winkelhorst" 16 | }, 17 | "location": { 18 | "street": "3288 hanengeschrei", 19 | "city": "barneveld", 20 | "state": "utrecht", 21 | "postcode": 59601, 22 | "coordinates": { 23 | "latitude": "42.4027", 24 | "longitude": "-124.0262" 25 | }, 26 | "timezone": { 27 | "offset": "-2:00", 28 | "description": "Mid-Atlantic" 29 | } 30 | }, 31 | "email": "elvis.winkelhorst@example.com", 32 | "login": { 33 | "uuid": "3f6541c8-a4c7-4f22-a335-493bb66652de", 34 | "username": "bluegoose340", 35 | "password": "christin", 36 | "salt": "MsXC9ikj", 37 | "md5": "6d4c13ac2d30b12cc1ad0ed8b64d0515", 38 | "sha1": "3113f3874d743bc50ddb0706a49b2ead6c8c210b", 39 | "sha256": "7fb078cf9ec85a6f280482fa1b26fa989f3f2d6ec594f4e899a70e6b5e8eb561" 40 | }, 41 | "dob": { 42 | "date": "1976-05-22T11:29:08Z", 43 | "age": 42 44 | }, 45 | "registered": { 46 | "date": "2005-09-21T19:21:15Z", 47 | "age": 12 48 | }, 49 | "phone": "(733)-431-8999", 50 | "cell": "(057)-383-4846", 51 | "id": { 52 | "name": "BSN", 53 | "value": "56620806" 54 | }, 55 | "picture": { 56 | "large": "https://randomuser.me/api/portraits/men/71.jpg", 57 | "medium": "https://randomuser.me/api/portraits/med/men/71.jpg", 58 | "thumbnail": "https://randomuser.me/api/portraits/thumb/men/71.jpg" 59 | }, 60 | "nat": "NL" 61 | } 62 | ], 63 | "info": { 64 | "seed": "63a6a9379d237d7d", 65 | "results": 1, 66 | "page": 1, 67 | "version": "1.2" 68 | } 69 | }, 70 | "rawHeaders": [ 71 | "Date", 72 | "Sun, 08 Jul 2018 10:10:12 GMT", 73 | "Content-Type", 74 | "application/json; charset=utf-8", 75 | "Transfer-Encoding", 76 | "chunked", 77 | "Connection", 78 | "close", 79 | "Set-Cookie", 80 | "__cfduid=d084fdd5424eb48556a1f43eb1f68216b1531044612; expires=Mon, 08-Jul-19 10:10:12 GMT; path=/; domain=.randomuser.me; HttpOnly", 81 | "X-Powered-By", 82 | "Express", 83 | "Access-Control-Allow-Origin", 84 | "*", 85 | "Cache-Control", 86 | "no-cache", 87 | "ETag", 88 | "W/\"452-pS0ngihKRl/4PYOvxKruyg\"", 89 | "Vary", 90 | "Accept-Encoding", 91 | "Expect-CT", 92 | "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", 93 | "Server", 94 | "cloudflare" 95 | ] 96 | } 97 | ] -------------------------------------------------------------------------------- /helpers/nock.js: -------------------------------------------------------------------------------- 1 | // Credit to https://github.com/NimaSoroush for this helper 2 | 3 | const nock = require('nock'); 4 | const path = require('path'); 5 | const zlib = require('zlib'); 6 | 7 | nock.back.fixtures = path.join(__dirname, '..', 'fixtures'); 8 | nock.back.setMode('record'); 9 | 10 | const makeCompressedResponsesReadable = scope => { 11 | if (scope.rawHeaders.indexOf('gzip') > -1) { 12 | const gzipIndex = scope.rawHeaders.indexOf('gzip'); 13 | scope.rawHeaders.splice(gzipIndex - 1, 2); 14 | 15 | const contentLengthIndex = scope.rawHeaders.indexOf('Content-Length'); 16 | scope.rawHeaders.splice(contentLengthIndex - 1, 2); 17 | 18 | const fullResponseBody = 19 | scope.response && 20 | scope.response.reduce && 21 | scope.response.reduce((previous, current) => previous + current); 22 | 23 | try { 24 | // eslint-disable-next-line no-param-reassign 25 | scope.response = JSON.parse( 26 | zlib.gunzipSync(Buffer.from(fullResponseBody, 'hex')).toString('utf8'), 27 | ); 28 | } catch (e) { 29 | // eslint-disable-next-line no-param-reassign 30 | scope.response = ''; 31 | } 32 | } 33 | return scope; 34 | }; 35 | 36 | const defaultOptions = { 37 | afterRecord: outputs => outputs.map(makeCompressedResponsesReadable), 38 | }; 39 | 40 | module.exports = defaultOptions; 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nock_example", 3 | "version": "1.0.0", 4 | "description": "Nock HTTP mocking example", 5 | "scripts": { 6 | "test": "jest" 7 | }, 8 | "author": "Dominic Fraser", 9 | "license": "ISC", 10 | "devDependencies": { 11 | "babel-eslint": "^8.2.5", 12 | "eslint": "^4.19.1", 13 | "eslint-config-airbnb": "^16.1.0", 14 | "eslint-config-prettier": "^2.9.0", 15 | "eslint-config-skyscanner": "^3.0.0", 16 | "eslint-plugin-import": "^2.13.0", 17 | "eslint-plugin-jsx-a11y": "^6.1.0", 18 | "eslint-plugin-prettier": "^2.6.2", 19 | "eslint-plugin-react": "^7.10.0", 20 | "jest": "^23.6.0", 21 | "nock": "^9.4.1", 22 | "prettier": "^1.13.7" 23 | }, 24 | "dependencies": { 25 | "node-fetch": "^2.1.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /userQueryHelper/defaultUser.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | results: [ 3 | { 4 | gender: 'male', 5 | name: { 6 | title: 'mr', 7 | first: 'rolf', 8 | last: 'hegdal', 9 | }, 10 | location: { 11 | street: 'ljan terrasse 346', 12 | city: 'vear', 13 | state: 'rogaland', 14 | postcode: '3095', 15 | coordinates: { 16 | latitude: '54.8646', 17 | longitude: '-97.3136', 18 | }, 19 | timezone: { 20 | offset: '-10:00', 21 | description: 'Hawaii', 22 | }, 23 | }, 24 | email: 'rolf.hegdal@example.com', 25 | login: { 26 | uuid: 'c4168eac-84b8-46ea-b735-c9da9bfb97fd', 27 | username: 'bluefrog786', 28 | password: 'ingrid', 29 | salt: 'GtRFz4NE', 30 | md5: '5c581c5748fc8c35bd7f16eac9efbb55', 31 | sha1: 'c3feb8887abed9ec1561b9aa2c9f58de21d1d3d9', 32 | sha256: 33 | '684c478a98b43f1ef1703b35b8bbf61b27dbc93d52acd515e141e97e04447712', 34 | }, 35 | dob: { 36 | date: '1975-11-12T06:34:44Z', 37 | age: 42, 38 | }, 39 | registered: { 40 | date: '2015-11-04T22:09:36Z', 41 | age: 2, 42 | }, 43 | phone: '66976498', 44 | cell: '40652479', 45 | id: { 46 | name: 'FN', 47 | value: '12117533881', 48 | }, 49 | picture: { 50 | large: 'https://randomuser.me/api/portraits/men/65.jpg', 51 | medium: 'https://randomuser.me/api/portraits/med/men/65.jpg', 52 | thumbnail: 'https://randomuser.me/api/portraits/thumb/men/65.jpg', 53 | }, 54 | nat: 'NO', 55 | }, 56 | ], 57 | info: { 58 | seed: '2da87e9305069f1d', 59 | results: 1, 60 | page: 1, 61 | version: '1.2', 62 | }, 63 | }; 64 | -------------------------------------------------------------------------------- /userQueryHelper/userQueryHelper.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const defaultUser = require('./defaultUser'); 3 | 4 | const throwNon200 = response => { 5 | if (!response.ok) { 6 | throw new Error(response.statusText); 7 | } 8 | return response; 9 | }; 10 | 11 | const getRandomUser = () => 12 | fetch('https://randomuser.me/api/') 13 | .then(throwNon200) 14 | .then(res => res.json()) 15 | .catch(e => console.log('Error getting new random user: ', e.message)); 16 | 17 | const getRandomUserOfNationality = n => 18 | fetch(`https://randomuser.me/api/?nat=${n}`) 19 | .then(throwNon200) 20 | .then(res => res.json()) 21 | .catch(e => console.log(e)); 22 | 23 | const getRandomUserGuarded = () => 24 | getRandomUser() 25 | .then(res => res || defaultUser) 26 | .catch(e => console.log(e)); 27 | 28 | module.exports = { 29 | getRandomUser, 30 | getRandomUserOfNationality, 31 | getRandomUserGuarded, 32 | }; 33 | -------------------------------------------------------------------------------- /userQueryHelper/userQueryHelper.test.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock'); 2 | const query = require('./userQueryHelper'); 3 | const defaultUser = require('./defaultUser'); 4 | const defaultOptions = require('../helpers/nock'); 5 | 6 | describe('userQueryHelper', () => { 7 | afterAll(nock.restore); 8 | afterEach(nock.cleanAll); 9 | 10 | it('should return a user (using fixtures - Promise syntax)', () => { 11 | nock.back.setMode('record'); 12 | 13 | return nock 14 | .back('random-user-data-promise.json', defaultOptions) 15 | .then(({ nockDone }) => 16 | query 17 | .getRandomUser() 18 | .then(res => 19 | expect(res).toEqual( 20 | expect.objectContaining({ 21 | results: [ 22 | { 23 | cell: expect.any(String), 24 | dob: expect.any(Object), 25 | email: expect.any(String), 26 | gender: expect.any(String), 27 | id: expect.any(Object), 28 | location: expect.any(Object), 29 | login: expect.any(Object), 30 | name: expect.any(Object), 31 | nat: expect.any(String), 32 | phone: expect.any(String), 33 | picture: expect.any(Object), 34 | registered: expect.any(Object), 35 | }, 36 | ], 37 | }), 38 | ), 39 | ) 40 | .then(nockDone), 41 | ) 42 | .then(() => nock.back.setMode('wild')); 43 | }); 44 | 45 | it('should return a user (using fixtures - Async/Await syntax)', async () => { 46 | nock.back.setMode('record'); 47 | const { nockDone } = await nock.back( 48 | 'random-user-data-async.json', 49 | defaultOptions, 50 | ); 51 | const userInfo = await query.getRandomUser(); 52 | 53 | expect(userInfo).toEqual( 54 | expect.objectContaining({ 55 | results: expect.any(Object), 56 | }), 57 | ); 58 | 59 | nockDone(); 60 | nock.back.setMode('wild'); 61 | }); 62 | 63 | it('should return a user', () => { 64 | nock('https://randomuser.me') 65 | .get('/api/') 66 | .reply(200, { 67 | results: [{ name: 'Dominic' }], 68 | }); 69 | 70 | return query 71 | .getRandomUser() 72 | .then(res => res.results[0].name) 73 | .then(res => expect(res).toEqual('Dominic')); 74 | }); 75 | 76 | it('should return a user of set nationality', () => { 77 | nock(/random/) 78 | .get(/nat=gb/) 79 | .reply(200, { 80 | results: [{ nat: 'GB' }], 81 | }); 82 | 83 | return query 84 | .getRandomUserOfNationality('gb') 85 | .then(res => res.results[0].nat) 86 | .then(res => expect(res).toEqual('GB')); 87 | }); 88 | 89 | it('should return a default user on 500', () => { 90 | nock(/randomuser/) 91 | .get(/api/) 92 | .reply(500); 93 | 94 | return query 95 | .getRandomUserGuarded() 96 | .then(res => expect(res).toMatchObject(defaultUser)); 97 | }); 98 | }); 99 | --------------------------------------------------------------------------------