{"id":17163928,"url":"https://github.com/igorlima/twitter-rest-api-server","last_synced_at":"2025-06-22T11:06:45.864Z","repository":{"id":28933931,"uuid":"32459659","full_name":"igorlima/twitter-rest-api-server","owner":"igorlima","description":"This is a public HTTP API to retrieve Twitter user timelines. An example for learning how to test our code in a manner to (i) inject mocks for other modules, (ii) to leak private variables or (iii) override variables within the module.","archived":false,"fork":false,"pushed_at":"2015-03-18T13:22:51.000Z","size":120,"stargazers_count":7,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-22T11:06:21.837Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/igorlima.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-03-18T13:16:21.000Z","updated_at":"2019-08-25T20:10:41.000Z","dependencies_parsed_at":"2022-09-06T13:41:45.483Z","dependency_job_id":null,"html_url":"https://github.com/igorlima/twitter-rest-api-server","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/igorlima/twitter-rest-api-server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorlima%2Ftwitter-rest-api-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorlima%2Ftwitter-rest-api-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorlima%2Ftwitter-rest-api-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorlima%2Ftwitter-rest-api-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/igorlima","download_url":"https://codeload.github.com/igorlima/twitter-rest-api-server/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igorlima%2Ftwitter-rest-api-server/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261282324,"owners_count":23134939,"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-10-14T22:50:25.851Z","updated_at":"2025-06-22T11:06:40.835Z","avatar_url":"https://github.com/igorlima.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Gittip Donate Button](http://img.shields.io/gratipay/igorribeirolima.svg)](https://gratipay.com/igorribeirolima/)\n\n# JS unit testing using dependency injection\n\nYou probably know that to do JavaScript testing is good and some hurdles to overcome is how to test our code in a manner to *(i)* inject mocks for other modules, *(ii)* to leak private variables or *(iii)* override variables within the module.\n\n[rewire](https://github.com/jhnns/rewire) is a tool for helping us on overcoming these hurdles. It provides us an easy way to dependency injection for unit testing and adds a special setter and getter to modules so we can modify their behaviour for better unit testing. What [rewire](https://github.com/jhnns/rewire) does is to not load the file and eval the contents to emulate the load mechanism.\n\nTo get started with dependency injection, we'll create [a twitter rest api server](https://gist.github.com/igorlima/b31f1a26a5b100186a98) and do unit tests using mocks and overriding variables within modules. This example will focus on back-end unit testing but if you want to use [rewire](https://github.com/jhnns/rewire) also on the client-side take a look at [client-side bundlers](https://github.com/jhnns/rewire#client-side-bundlers).\n\n## An example\n\nThis example is a public HTTP API to retrieve Twitter user timelines. It has basically two files: [server.js](https://gist.github.com/igorlima/b31f1a26a5b100186a98#file-server-js) and [twitter.js](https://gist.github.com/igorlima/b31f1a26a5b100186a98#file-twitter-js).\n\nThe first file creates [an basic instance](http://expressjs.com/starter/hello-world.html) of [express](http://expressjs.com) and defines [a route for a GET request method](http://expressjs.com/starter/basic-routing.html), which is ``/twitter/timeline/:user``.\n\nThe second one is a module responsible for retrieving data from Twitter. It requires:\n\n* [**twit**](https://github.com/ttezel/twit): Twitter API Client for node\n* [**async**](https://github.com/caolan/async): is a utility module which provides straight-forward, powerful functions for working with asynchronous JavaScript\n* [**moment**](http://momentjs.com): a lightweight JavaScript date library for parsing, validating, manipulating, and formatting dates.\n\nPart of these modules will be mocked and overridden in our tests.\n\n## Running the example\n\nThis example is already running [in a cloud](https://social-media-rest-api.herokuapp.com). So you can reach the urls below and see it working:\n\n* [igorribeirolima timeline](https://social-media-rest-api.herokuapp.com/twitter/timeline/igorribeirolima)\n* [strongloop timeline](https://social-media-rest-api.herokuapp.com/twitter/timeline/strongloop)\n* [tableless timeline](https://social-media-rest-api.herokuapp.com/twitter/timeline/tableless)\n\nTo run it locally, clone [this gist](https://gist.github.com/igorlima/b31f1a26a5b100186a98) by ``git clone https://gist.github.com/b31f1a26a5b100186a98.git twitter-rest-api-server`` and set five environment variables. Those envs are listed below. For secure reason I won't share my token. To get yours, access [Twitter developer documentation](https://dev.twitter.com/overview/documentation), [create a new app](https://apps.twitter.com) and set up your credentials.\n\nFor [Mac users](http://stackoverflow.com/questions/7501678/set-environment-variables-on-mac-os-x-lion), you can simply type:\n\n```\nexport TwitterConsumerKey=\"xxxx\"\nexport TwitterConsumerSecret=\"xxxx\"\nexport TwitterAccessToken=\"xxxx\"\nexport TwitterAccessTokenSecret=\"xxxx\"\nexport MomentLang=\"pt-br\"\n```\n\nFor [Windows users](http://stackoverflow.com/questions/21606419/set-windows-environment-variables-with-commandline-cmd-commandprompt-batch-file), do:\n\n```\nSET TwitterConsumerKey=\"xxxx\"\nSET TwitterConsumerSecret=\"xxxx\"\nSET TwitterAccessToken=\"xxxx\"\nSET TwitterAccessTokenSecret=\"xxxx\"\nSET MomentLang=\"pt-br\"\n```\n\nAfter setting up the environment variables, go to ``twitter-rest-api-server`` folder, install all node dependencies by ``npm install``, then run via terminal ``node server.js``. It should be available at the port ``5000``. Go to your browser and reach ``http://localhost:5000/twitter/timeline/igorribeirolima``.\n\n![running express app example locally](http://i1368.photobucket.com/albums/ag182/igorribeirolima/running%20express%20app%20example%20locally_zpsndaidg4w.png)\n\n## Writing unit tests\n\n[Mocha](http://mochajs.org) is the JavaScript test framework running we gonna use. It makes asynchronous testing simple and fun. [Mocha](http://mochajs.org) allows you to use any assertion library you want, if it throws an error, it will work! In this example we are gonna utilize [node's regular assert](https://nodejs.org/api/assert.html) module.\n\nImagine you want to test this code [twitter.js](https://gist.github.com/igorlima/b31f1a26a5b100186a98#file-twitter-js):\n\n```javascript\nvar Twit   = require('twit'),\n    async  = require('async'),\n    moment = require('moment'),\n    T      = new Twit({\n      consumer_key: process.env.TwitterConsumerKey || '...',\n      consumer_secret: process.env.TwitterConsumerSecret || '...',\n      access_token: process.env.TwitterAccessToken || '...',\n      access_token_secret: process.env.TwitterAccessTokenSecret || '...'\n    }),\n    \n    mapReducingTweets = function(tweet, callback) {\n      callback(null, simplify(tweet));\n    },\n\n    simplify = function(tweet) {\n      var date = moment(tweet.created_at, \"ddd MMM DD HH:mm:ss zz YYYY\");\n      date.lang( process.env.MomentLang );\n      return {\n        date: date.format('MMMM Do YYYY, h:mm:ss a'),\n        id: tweet.id,\n        user: {\n          id: tweet.user.id\n        },\n        tweet: tweet.text\n      };\n    };\n\nmodule.exports = function(username, callback) {\n  T.get(\"statuses/user_timeline\", {\n    screen_name: username,\n    count: 25\n  }, function(err, tweets) {\n    if (err) callback(err);\n    else async.map(tweets, mapReducingTweets, function(err, simplified_tweets) {\n      callback(null, simplified_tweets);\n    });\n  })\n};\n```\n\nTo do that in a easy and fun way, let load this module using [rewire](https://github.com/jhnns/rewire). So within your test module [twitter-spec.js](https://gist.github.com/igorlima/b31f1a26a5b100186a98#file-twitter-spec-js):\n\n```javascript\nvar rewire  = require('rewire'),\n    assert  = require('assert'),\n    twitter = rewire('./twitter.js'),\n    mock    = require('./twitter-spec-mock-data.js');\n```\n\n[rewire](https://github.com/jhnns/rewire) acts exactly like *require*. Just with one difference: Your module will now export a special setter and getter for private variables.\n\n```javascript\nmyModule.__set__(\"path\", \"/dev/null\");\nmyModule.__get__(\"path\"); // = '/dev/null'\n```\n\nThis allows you to mock everything in the top-level scope of the module, like the *twitter module* for example. Just pass the variable name as first parameter and your mock as second.\n\nYou may also override globals. These changes are only within the module, so you don't have to be concerned that other modules are influenced by your mock.\n\n```javascript\ndescribe('twitter module', function(){\n\n  describe('simplify function', function(){\n    var simplify;\n   \n    before(function() {\n      simplify = twitter.__get__('simplify');\n    });\n\n    it('should be defined', function(){\n      assert.ok(simplify);\n    });\n\n    describe('simplify a tweet', function(){\n      var tweet, mock;\n     \n      before(function() {\n        mock = mocks[0];\n        tweet = simplify(mock);\n      });\n\n      it('should have 4 properties', function() {\n        assert.equal( Object.keys(tweet).length, 4 );\n      });\n\n      describe('format dates as `MMMM Do YYYY, h:mm:ss a`', function() {\n\n        describe('English format', function() {\n          before(function() {\n            revert = twitter.__set__('process.env.MomentLang', 'en');\n            tweet = simplify(mock);\n          });\n\n          it('should be `March 6th 2015, 2:29:13 am`', function() {\n            assert.equal(tweet.date, 'March 6th 2015, 2:29:13 am');\n          });\n\n          after(function(){\n            revert();\n          });\n\n        });\n\n        describe('Brazilian format', function() {\n          before(function() {\n            revert = twitter.__set__('process.env.MomentLang', 'pt-br');\n            tweet = simplify(mock);\n          });\n\n          it('should be `Março 6º 2015, 2:29:13 am`', function() {\n            assert.equal(tweet.date, 'Março 6º 2015, 2:29:13 am');\n          });\n\n          after(function(){\n            revert();\n          });\n          \n        });\n\n      });\n\n    });\n\n  });\n  \n  describe('retrieve timeline feed', function() {\n    var revert;\n    before(function() {\n      revert = twitter.__set__(\"T.get\", function( api, query, callback ) {\n        callback( null, mocks);\n      });\n    });\n\n    describe('igorribeirolima timeline', function() {\n      var tweets;\n      before(function(done){\n        twitter('igorribeirolima', function(err, data) {\n          tweets = data;\n          done();\n        });\n      });\n\n      it('should have 19 tweets', function() {\n        assert.equal(tweets.length, 19);\n      });\n\n    });\n\n    after(function() {\n      revert();\n    });\n\n   });\n});\n```\n\n`__set__` returns a function which reverts the changes introduced by this particular `__set__` call.\n\n\n## Running unit tests\n\nBefore we get into the test and walk through it, let install mocha CLI by ``npm install -g mocha``. It will support us on running our tests just typing ``mocha twitter-spec.js``. The following is an image that illustrates the test result.\n\n![image that ilustrates unit tests running](http://i1368.photobucket.com/albums/ag182/igorribeirolima/running%20unit%20tests_zpshqazy5po.png)\n\nTake a look on this [video](http://showterm.io/3c970843502e140bcfabd#slow) and see step by step in detail everything discussed so far.\n\n## Conclusion \n\nAs you can see it's not painful on overcoming hurdles like *(i)* injecting mocks for other modules, *(ii)* leaking private variables or *(iii)* overriding variables within the module. That's it folks. Hope you catch the idea on how simple and fun is to do dependency injection for unit testing. Thanks for reading.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figorlima%2Ftwitter-rest-api-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Figorlima%2Ftwitter-rest-api-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figorlima%2Ftwitter-rest-api-server/lists"}