Showing posts with label module. Show all posts
Showing posts with label module. Show all posts

Saturday, December 12, 2015

Least Astonishment: Node.js mutable module cache

Node.js' require cache's modules after the initial call to `require`, so that subsequent calls to require return the cached module.  If a module exports an object, and that object is mutated then subsequent calls to require that module will import the mutated object.  Below are 3 files.  One exporting an object, one requiring that object and mutating it, and another re-requiring the object, which loads the mutated object from the cache:



shared.js exports an object, which will be required and mutated.

mutate-require.js imports the object from shared.js.  This is the first import so it is loading the object that shared.js exports fresh and stores it in the cache.  It then mutates that object, and exports null.

Finally index.js imports mutate-require.js, which triggers the require of shared.js, the imports shared.js directly, which results from a cache load.  The comment at the bottom of index.js shows the output of when index.js is executed.

Is this the behavior you'd expect?  I was surprised, when I saw this.  I could imagine a more sane default being: having `require` copy the exported value to the cache and returning the clean copy on each require. But then what about modules that export objects that mutate themselves??  Thoughts?

Sunday, November 29, 2015

Node.js Spying on Exported Module Property

Popular node unit testing frameworks emphasize both spying and mocking in unittests.  Because so much IO regularly occurs in node projects, planning a strategy to mock on IO in unittests should be a high priority.   When testing it's usually pretty clear what methods need to be spied on (any that make IO requests :)  But if code isn't architected to be testable from the beginning, then spies, mocks and patches end up being applied in roundabout ways.  Lately I've been seeing a common mistake made with spying on exported module objects.  This blog posts assumes understanding of testing spies.

What are some ways to spy on exported module methods?

Assume we have a module resources.js.  All of its methods are being exported for unittesting.

buildRequest is easily tested because it has no IO calls.  The difficulty arises when we try to test getData and makeRequest.  Our goal for this blog is to write a test that asserts getData calls makeRequest a single time with the correct arguments.  Since makeRequest performs IO, and IO is a no-no for our unittests, we have to mock it in some way.  Our fist attempt at doing so is:

In our test resources.js is required and then makeRequest is mocked and spied on, so the test can make assertions on it, and no IO is performed.  The output of running the above test is provided, which results in an error.  This is the initial attempt that I've been seeing very frequently.  The issue is the spy is being created on the exported objects property and NOT the makeRequest defined in and used in resources.js.

To illustrate this; if resources.js were to use the object it is exporting in its calls, then the spy would be created and used as expected!!!
The above sample modifies resources.js to use the object it is exporting, which is the same object the spy is being applied to.  The output of running the test in test.spec.js against the above resources.js file is shown. The test PASSES! (In the near future I plan on having a blog that shows, where, why, how objects are exported/required referencing node core code)

While the above works, I personally don't think it is very clear, (and haven't really seen modules that use module.exports in its function implementations.  I also think it is dangerous to reference module.exports internally to a module because, clients of that module can mutate it!!!!

A similar way to mock makeRequest is to export a reference to an object used internally.
The above code, defines a utils object in resources.js and exports a reference to it.  Because it is a reference, the test can be updated to spy on resources.utils.makeRequest and the utils object in resources.js will be mutated.

I think this way is cleaner than using module.exports directly, but is still susceptible to being mutated by a client!

Dependency injection is a strong tool for creating node code that can be easily tested; by providing a clean way to mock IO dependencies.  It requires that the caller provides (injects) a functions dependencies.  In this case getData depends on functions that perform IO, makeRequest.  Refactoring it would require the caller of getData to provide a makeRequest function.  This would seamlessly allow a test to provide a mocked makeRequest method (that doesn't make IO calls), and the actual code to provide a different makeRequest method (which does make IO calls).
getData now requires that a requestor method is provided by the client.  The test code is free to provide a spy as the requestor, while the production clients will be required to provide makeRequest

getData(resource, resources.makeRequest, callback);

For the above example, getData only has a single dependency, while frequently in real world code methods may have multiple IO dependencies.  While functions can usually be decomposed and refactored to achieve making a single call or two, doing so after code is already in production is dangerous.  Because of how easy it is to create extremely nested node.js code it is very beneficial to design testable code from the start.  A powerful tool to do this is dependency injection, something I plan on writing a lot more about very soon.

Keep testing, happy noding!!!