{"id":1,"date":"2015-03-13T23:52:20","date_gmt":"2015-03-13T23:52:20","guid":{"rendered":"http:\/\/www.mattritter.me\/?p=1"},"modified":"2015-03-14T03:53:47","modified_gmt":"2015-03-14T03:53:47","slug":"hello-world","status":"publish","type":"post","link":"https:\/\/www.mattritter.me\/?p=1","title":{"rendered":"Unit Testing Express Routes with Mongoose and Jasmine"},"content":{"rendered":"<p>I\u2019ve been working for a while on a project called <a title=\"Repcoin\" href=\"http:\/\/www.repcoin.com\/\" target=\"_blank\">Repcoin<\/a>. It&#8217;s a web application that uses <a href=\"http:\/\/expressjs.com\/\">Express<\/a>, <a href=\"https:\/\/nodejs.org\/\">NodeJS<\/a>, <a href=\"http:\/\/mongoosejs.com\/\">Mongoose<\/a>, and <a href=\"http:\/\/www.mongodb.com\/\">MongoDB<\/a> on the backend.\u00a0As the Repcoin <a href=\"https:\/\/github.com\/krazemon\/repcoin\" target=\"_blank\">codebase<\/a> has grown, we\u2019ve become increasingly dependent on unit tests for sanity\u2019s sake. One way to reassure that the code paths in our routes work is by unit testing with <a href=\"http:\/\/jasmine.github.io\/\">Jasmine<\/a>.<\/p>\n<p>Every request that hits our backend goes to the appropriate router. Consider a simplified version of the <a href=\"https:\/\/github.com\/krazemon\/repcoin\/blob\/master\/api\/routes\/UserRoutes.js\">UserRouter<\/a>, which handles queries related to the User Schema:<\/p>\n<pre class=\"attributes\" title=\"A snippet of the user routes\" lang=\"javascript\">var UserHandler = require('..\/handlers\/user.js');\r\n\r\nmodule.exports = function(router) {\r\n  router.get('\/users\/:user_id', UserHandler.users.userId.get);\r\n};\r\n<\/pre>\n<p>From here, the request is sent to the <a href=\"https:\/\/github.com\/krazemon\/repcoin\/blob\/master\/api\/handles\/user.js\">UserHandler<\/a>, which takes care of the real logic. This route might look like this:<\/p>\n<pre class=\"attributes\" title=\"A snippet of the user handler\" lang=\"javascript\">var User = require('..\/models\/User.js');\r\n\r\nvar UserHandler = {\r\n  ........\r\n    userId: {\r\n      get: function(req, res) {\r\n        User.findById(userId).exec().then(function(user) {\r\n          return res.status(200).send(user);\r\n        }, function(err) {\r\n          return res.status(500).send(err);\r\n        });\r\n      },\r\n    },\r\n  ........\r\n};\r\n\r\nmodule.exports = UserHandler;\r\n<\/pre>\n<p>It would be nice if we could unit test the paths here and make sure that a response will certainly be returned. But, there&#8217;s some gritty code to mock up.<\/p>\n<p>First is the Mongoose promise returned by <code>findById()<\/code>. The promise returned by calling <code>exec()<\/code> is executed by calling <code>then()<\/code>, which takes a callback for success and a callback for failure.<\/p>\n<p>The other issue comes from the request and response objects. Express packs these variables with functionality, but we just need some simple mocks. Here&#8217;s how we can solve this:<\/p>\n<p>Jasmine provides <code>beforeEach()<\/code> and <code>afterEach()<\/code>, which will run before and after a given test or set of tests. By declaring our req and res at the top of the file, we can mock the functionality we want like this:<\/p>\n<pre class=\"attributes\" title=\"Mocking express res and req\" lang=\"javascript\">var req, res;\r\nbeforeEach(function() {\r\n  req = {\r\n    query: {},\r\n    params: {},\r\n    body: {},\r\n  };\r\n\r\n  res = {\r\n    status: jasmine.createSpy().andCallFake(function(msg) {\r\n      return this;\r\n    }),\r\n    send: jasmine.createSpy().andCallFake(function(msg) {\r\n      return this;\r\n    })\r\n  };\r\n});\r\n\r\nafterEach(function() {\r\n  expect(res.status.callCount).toEqual(1);\r\n  expect(res.send.callCount).toEqual(1);\r\n});\r\n<\/pre>\n<p>Now, we have simple req and res objects that will perform the functionality we need. We can confirm that the calls to <code>send()<\/code> and <code>status()<\/code> happen with the proper arguments on a per-test basis, which I will show in a moment.<\/p>\n<p>The other issue was the Mongoose promise. We can create a mock promise that looks like this:<\/p>\n<pre class=\"attributes\" title=\"A successful mock Mongoose promise\" lang=\"javascript\">var userPromise = {\r\n  exec: function() {\r\n    return {\r\n      then: function(cbS, cbF) { return cbS({ username: 'Matt', _id: '123' }); };\r\n    };\r\n  },\r\n};\r\n<\/pre>\n<p>This userPromise will execute successfully. If we wanted to have the promise fail, we could do that like this:<\/p>\n<pre class=\"attributes\" title=\"An unsuccessful mock Mongoose promise\" lang=\"javascript\">var userPromise = {\r\n  exec: function() {\r\n    return {\r\n      then: function(cbS, cbF) { return cbF('Error!!'); };\r\n    };\r\n  },\r\n};\r\n<\/pre>\n<p>Now we finally have our mocked up variables. We can include these in any of our tests to make sure errors are handled properly. A success test might look like so:<\/p>\n<pre class=\"attributes\" title=\"Example unit test\" lang=\"javascript\">describe('get: ', function() {\r\n  it('successfully gets the user', function() {\r\n    spyOn(User, 'findById').andReturn(userPromise);\r\n    req.params = { user_id: '123' };\r\n    UserHandler.users.userId.get(req, res);\r\n    expect(User.findByIdPublic.callCount).toEqual(1);\r\n    expect(res.status).toHaveBeenCalledWith(200);\r\n    expect(res.send).toHaveBeenCalledWith({ username: 'Matt', _id: '123' });\r\n  });\r\n});\r\n<\/pre>\n<p>And that&#8217;s all there is to it. A more full-stack test that takes advantage of the Express <a href=\"http:\/\/expressjs.com\/guide\/using-middleware.html\">middleware<\/a> could be done with a tool like <a href=\"https:\/\/github.com\/visionmedia\/supertest\">supertest<\/a>, but this is a good way to unit test a single function.<\/p>\n<p>If you&#8217;re interested in digging deeper into the Repcoin code, check out the <a href=\"https:\/\/github.com\/krazemon\/repcoin\">Github<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<a href=\"https:\/\/www.mattritter.me\/?p=1\" rel=\"bookmark\" title=\"Permalink to Unit Testing Express Routes with Mongoose and Jasmine\"><p>I\u2019ve been working for a while on a project called Repcoin. It&#8217;s a web application that uses Express, NodeJS, Mongoose, and MongoDB on the backend.\u00a0As the Repcoin codebase has grown, we\u2019ve become increasingly dependent on unit tests for sanity\u2019s sake. One way to reassure that the code paths in our routes work is by unit [&hellip;]<\/p>\n<\/a>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"nf_dc_page":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[1],"tags":[],"class_list":["post-1","post","type-post","status-publish","format-standard","category-uncategorized","h-entry","hentry"],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p5T9DB-1","jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/www.mattritter.me\/index.php?rest_route=\/wp\/v2\/posts\/1","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mattritter.me\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mattritter.me\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mattritter.me\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mattritter.me\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1"}],"version-history":[{"count":19,"href":"https:\/\/www.mattritter.me\/index.php?rest_route=\/wp\/v2\/posts\/1\/revisions"}],"predecessor-version":[{"id":30,"href":"https:\/\/www.mattritter.me\/index.php?rest_route=\/wp\/v2\/posts\/1\/revisions\/30"}],"wp:attachment":[{"href":"https:\/\/www.mattritter.me\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mattritter.me\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mattritter.me\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}