{"id":8259,"date":"2017-01-10T12:41:43","date_gmt":"2017-01-10T09:41:43","guid":{"rendered":"http:\/\/railsware.com\/blog\/?p=8259"},"modified":"2017-12-20T23:33:17","modified_gmt":"2017-12-20T20:33:17","slug":"mocking-es6-module-import-without-dependency-injection","status":"publish","type":"post","link":"https:\/\/railsware.com\/blog\/mocking-es6-module-import-without-dependency-injection\/","title":{"rendered":"Mocking ES6 module import with and without Dependency Injection"},"content":{"rendered":"<p class=intro-text>ES6 module system is a great addition to JavaScript language, but sometimes it&#8217;s at odds with writing tests, because you need to mock imported function.<\/p>\n\n<p>Usual solution is to use Dependency Injection akin to what is used in <code>Angular.JS<\/code>. You need to wrap the whole module into some function and explicitly pass required dependencies. While such approach works fine, it mangles code and adds an additional layer of indirection.<\/p>\n\n<p>There are two alternative approaches. One is by using mocking library, which is simpler. Another one is more complex and uses Dependency Injection, but in a way, which does not cause big changes to initial code. Using mocking library works fine at the moment, because ES6 modules are transpiled into ES5 objects. But it could not work with real ES6 modules and then Dependency Injection way can be handy.<\/p>\n\n<h2>Mocking with Sinon.JS<\/h2>\n\n<p>Let&#8217;s consider <span class=\"crayon-inline\">feature.js<\/span> module, which looks like this:<\/p>\n\n<pre class=\"lang:javascript\">\n\/\/ feature.js module\nimport { fetchData } from '.\/backend';\n\nexport function doSomething() {\n  \/\/ some code which calls fetchData\n}\n<\/pre>\n\n<p><span class=\"crayon-inline\">feature.js<\/span> imports <span class=\"crayon-inline\">fetchData<\/span> function from <span class=\"crayon-inline\">backend.js<\/span>. The goal is to mock <span class=\"crayon-inline\">fetchData<\/span> call in <span class=\"crayon-inline\">feature.js<\/span> when writing functional tests. In our example, we will use <a href=\"http:\/\/sinonjs.org\/\"><strong>Sinon.JS<\/strong><\/a>, but <a href=\"https:\/\/jasmine.github.io\/\"><strong>Jasmine<\/strong><\/a> can be used as well.<\/p>\n\n<pre class=\"lang:javascript\">\n\/\/ feature.test.js module\nimport { expect } from 'chai';\nimport sinon from 'sinon';\nimport { doSomething } from '.\/feature';\nimport * as Backend from '.\/backend';\n\ndescribe.only('doSomething', () => {\n  it('does something meaningful', () => {\n    sinon.stub(Backend, 'fetchData');\n    \/\/ use doSomething with mocked fetchData\n  });\n});\n<\/pre>\n\n<p>So, the main trick here is to import the whole module:<\/p>\n\n<pre class=\"lang:javascript toolbar:2 wrap:true nums:false\">\nimport * as Backend from '.\/backend';\n<\/pre>\n\n<p>and stub\/mock required call:<\/p>\n\n<pre class=\"lang:javascript toolbar:2 wrap:true nums:false\">\nsinon.stub(Backend, 'fetchData');\n<\/pre>\n\n<h2>Mocking with Dependency Injection<\/h2>\n\n<p>To manually mock the function, the simplest way would be to reassign <span class=\"crayon-inline\">fetchData<\/span> to some mock-function, but imported bindings are read-only. So, we need to make a little trick:<\/p>\n\n<pre class=\"lang:javascript\">\n\/\/ feature.js module\nimport { fetchData as originalFetchData } from '.\/backend';\n\nlet fetchData = originalFetchData;\n\nexport function mock(mockedFetchData) {\n  fetchData = mockedFetchData || originalFetchData;\n}\n\nexport function doSomething() {\n  \/\/ some code which calls fetchData\n}\n<\/pre>\n\n<p>Now, instead of directly using <span class=\"crayon-inline\">fetchData<\/span> from <span class=\"crayon-inline\">backend.js<\/span> we imported it as <span class=\"crayon-inline\">originalFetchData<\/span>. Our local variable <span class=\"crayon-inline\">fetchData<\/span> can be reassigned to anything we want. By default it is bound to <span class=\"crayon-inline\">originalFetchData<\/span>. To mock it we need to call <span class=\"crayon-inline\">mock<\/span> and pass a mocked version. In case we need to unmock, just call <span class=\"crayon-inline\">mock<\/span> with no params.<\/p>\n\n<p>An example of usage in test:<\/p>\n\n<pre class=\"lang:javascript\">\n\/\/ feature.test.js module\nimport { expect } from 'chai';\nimport { mock, doSomething } from '.\/feature';\n\nconst dummyFetchData = () => {};\n\ndescribe('doSomething', () => {\n  beforeEach(() => { mock(dummyFetchData); });\n  afterEach(() => { mock(); });\n\n  it('does something meaningful', () => {\n    \/\/ use doSomething with mocked fetchData\n  });\n});\n<\/pre>\n\n<h2>Mocking Module<\/h2>\n\n<p>There is a slight variation of this approach, if we need to mock a set of calls from one module:<\/p>\n\n<pre class=\"lang:javascript\">\n\/\/ feature.js module\nimport * as Backend from '.\/backend';\n\nlet { fetchData, saveData, deleteData } = Backend;\n\nexport function mock(mockedBackend) {\n  ({ fetchData, saveData, deleteData } = mockedBackend || Backend);\n}\n\n\/\/ some code which calls functions from Backend\n<\/pre>\n\n<p>Here we import the whole module and cherry-pick required calls. The rest is the same as before, but we need to pass an object with mocked calls into <span class=\"crayon-inline\">mock<\/span>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>ES6 module system is a great addition to JavaScript language, but sometimes it&#8217;s at odds with writing tests, because you need to mock imported function. Usual solution is to use Dependency Injection akin to what is used in Angular.JS. You need to wrap the whole module into some function and explicitly pass required dependencies. While&#8230;<\/p>\n","protected":false},"author":25,"featured_media":9418,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[3],"tags":[],"coauthors":["Sergii Boiko"],"class_list":["post-8259","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"acf":[],"aioseo_notices":[],"categories_data":[{"name":"Engineering","link":"https:\/\/railsware.com\/blog?category=development"}],"post_thumbnails":"https:\/\/railsware.com\/blog\/wp-content\/themes\/railsware\/vendors\/images\/article-thumbnail-default.jpg","amp_enabled":true,"_links":{"self":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/8259","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/users\/25"}],"replies":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/comments?post=8259"}],"version-history":[{"count":45,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/8259\/revisions"}],"predecessor-version":[{"id":13519,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/8259\/revisions\/13519"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/media\/9418"}],"wp:attachment":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/media?parent=8259"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/categories?post=8259"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/tags?post=8259"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/coauthors?post=8259"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}