I've encountered this myself in personal projects. With tests you will catch it early of course, but I'm not sure if there is a linter/tool that will catch this early on to save grief later....
On Mon, Dec 16, 2013 at 4:49 AM, Patrick Mueller <pmue...@gmail.com> wrote: > What I've learned from this is to be careful when you use `module.exports > =`. > > It doesn't come up much, because it's only a problem (I think) when there > is some mutual recursion in your require() trees. I've done this to myself > when building packages where I was exporting functions via `module.exports > =` as a common pattern, and then "crossed the paths" somewhere. > > Note that this is a dev-time problem, and not a run-time problem. Assuming > you are testing all your code, you will find problems with this pattern > VERY EARLY. So, it's nothing to be scared of or outlaw entirely. It's > never going to "pop up" unexpectedly at runtime. > > It is, unfortunately, a bit of a mysterious bug, when it does happen. So I > wanted others to be aware of this one weird problem with the pattern. > > > > On Sun, Dec 15, 2013 at 8:08 PM, Brian LeRoux <b...@brian.io> wrote: > > > Super interesting, I am surprised this doesn't come up more often really. > > > > I suppose the moral of the story is to avoid executing code in your > modules > > outside of the scope of things you are exporting. > > > > > > On Mon, Dec 16, 2013 at 3:46 AM, Patrick Mueller <pmue...@gmail.com> > > wrote: > > > > > The big problem with exporting functions - or exporting anything by > using > > > `module.exports =` style of exporting - is recursive require problem. > I > > > couldn't quickly find any ref to this on the web, so did a little gist: > > > https://gist.github.com/pmuellr/7975152 > > > > > > > > > On Thu, Dec 12, 2013 at 7:37 PM, Brian LeRoux <b...@brian.io> wrote: > > > > > > > Oh interesting. I can see what you mean though I think the only > thing I > > > > should know about the output is the return value. (In your case, > just a > > > > test for 'done'.) The behavior of the module is super important of > > > course. > > > > In the small modules philosophy the foo, bar.method, and baz would > have > > > > thier own tests for interface/output to satisfy the case you > describe. > > > > > > > > Again, IT DEPENDS! The rimraf example is a perfect case for what you > > > > describe. > > > > > > > > > > > > On Fri, Dec 13, 2013 at 10:59 AM, Gord Tanner <gtan...@gmail.com> > > wrote: > > > > > > > > > It depends what you define as outputs. > > > > > > > > > > in a given module: > > > > > > > > > > var foo = require('foo'), bar = require('bar'), baz = > require('baz'); > > > > > > > > > > module.exports = function(a, b) { > > > > > foo(3); > > > > > bar.method(a); > > > > > baz(b); > > > > > > > > > > return "done"; > > > > > } > > > > > > > > > > I have always counted the calls to foo, bar and baz as output that > > > needs > > > > to > > > > > be tested. This would produce a spec like: > > > > > > > > > > "when calling this module": > > > > > "it calls foo with 3" > > > > > "it calls bar.method with a" > > > > > "it calls baz with b" > > > > > "it returns done" > > > > > > > > > > It is just easier to mock bar.method then foo > > > > > > > > > > ie: > > > > > > > > > > var rewire = require('rewire'); > > > > > var example = rewire('example'); //NOTE: rewire rather then > > require > > > > > example.__set__('foo', jasmine.createSpy()); > > > > > > > > > > vrs: > > > > > > > > > > var example = require('example'); > > > > > var bar = require('bar'); > > > > > spyOn(bar, "method"); > > > > > > > > > > I came across this problem when using one of Isaac's modules > (rimraf > > > [1]) > > > > > where I obviously didn't want to call that in a unit test from my > > > module > > > > > but I need to mock it out. Rewire was the only way I could. > > > > > > > > > > [1] - https://github.com/isaacs/rimraf > > > > > > > > > > > > > > > > > > > > On Thu, Dec 12, 2013 at 6:37 PM, Brian LeRoux <b...@brian.io> wrote: > > > > > > > > > > > ALSO: lets avoid using terms like 'I agree' or 'I disagree'. Its > > > > > > programming. The answer is ALWAYS 'it depends'. No absolutes in > the > > > sea > > > > > of > > > > > > change. > > > > > > > > > > > > > > > > > > On Fri, Dec 13, 2013 at 10:34 AM, Brian LeRoux <b...@brian.io> > wrote: > > > > > > > > > > > > > Maybe. Have a look at Substack's code and you'll see he has no > > > > trouble > > > > > > > testing. The reason being he tests interfaces and outputs > instead > > > of > > > > > > > implementations. That will be another Node 101! > > > > > > > > > > > > > > > > > > > > > On Fri, Dec 13, 2013 at 10:24 AM, Gord Tanner < > gtan...@gmail.com > > > > > > > > wrote: > > > > > > > > > > > > > >> I also agree with this except for returning a function from > > > > > > >> module.exports. > > > > > > >> > > > > > > >> It is possible but makes mocking much much harder for testing. > > > > > > >> > > > > > > >> think of: > > > > > > >> > > > > > > >> > > > > > > >> var foo = require('foo'); > > > > > > >> > > > > > > >> module.exports = { > > > > > > >> awesome: function (a) { > > > > > > >> foo(a+1); > > > > > > >> } > > > > > > >> }; > > > > > > >> > > > > > > >> It is kind of awkward to test this module's use of the foo > > module. > > > > It > > > > > > can > > > > > > >> be done with rewire [1] but is a little awkward. > > > > > > >> > > > > > > >> If foo was designed where it exported an object literal with > > > > functions > > > > > > it > > > > > > >> would be much easier to mock: > > > > > > >> > > > > > > >> var foo = require('foo'); > > > > > > >> > > > > > > >> module.exports = { > > > > > > >> awesome: function (a) { > > > > > > >> foo.bar(a+1); > > > > > > >> } > > > > > > >> }; > > > > > > >> > > > > > > >> it("calls foo.bar", function () { > > > > > > >> var foo = require('foo'); > > > > > > >> spyOn(foo, "bar"); > > > > > > >> }); > > > > > > >> > > > > > > >> Just my 2 cents from a testing perspective. > > > > > > >> > > > > > > >> > > > > > > >> [1] - https://github.com/jhnns/rewire > > > > > > >> > > > > > > >> > > > > > > >> On Thu, Dec 12, 2013 at 6:06 PM, Brian LeRoux <b...@brian.io> > > wrote: > > > > > > >> > > > > > > >> > Create modules that are the smallest possible unit of code. > > Less > > > > > code > > > > > > is > > > > > > >> > fast code. Faster to write. Faster to maintain. Faster to > > test. > > > On > > > > > the > > > > > > >> > extreme end characters in the Node community such as > Substack > > > > > > advocate a > > > > > > >> > single function per module definition. > > > > > > >> > > > > > > > >> > module.exports = function() { > > > > > > >> > // my logic here > > > > > > >> > } > > > > > > >> > > > > > > > >> > This is kind of extreme and not always possible but a good > > > > practice > > > > > > >> > nonetheless. The idea is not new. Its a part of the UNIX > > > > philosophy: > > > > > > "do > > > > > > >> > one thing well" coined by Doug Mcilroy. [1] > > > > > > >> > > > > > > > >> > It can help you make code that looks like this [2] into this > > > [3]. > > > > > > >> > > > > > > > >> > > > > > > > >> > > > > > > > >> > [1] > > > > > > > http://homepage.cs.uri.edu/~thenry/resources/unix_art/ch01s06.html > > > > > > >> > [2] > > > > > > >> > > > > > > > >> > > > > > > > >> > > > > > > > > > > > > > > > > > > > > > https://github.com/apache/cordova-js/blob/c320378b484a172a02d3ee26634bcc584f43b939/Gruntfile.js > > > > > > >> > [3] > > > https://github.com/apache/cordova-js/blob/master/Gruntfile.js > > > > > > >> > > > > > > > >> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > -- > > > Patrick Mueller > > > http://muellerware.org > > > > > > > > > -- > Patrick Mueller > http://muellerware.org >