xolvio / qualityfaster

An example project showing how to create robust and maintainable acceptance tests
262 stars 58 forks source link

Require failure using wallaby_server with wallaby and coffeescript #52

Open jdrucza opened 8 years ago

jdrucza commented 8 years ago

I'm trying to get wallaby working for server tests for a meteor app that uses coffeescript. So I tried to use the wallaby_server file from this repo however it fails because it can't find any meteor modules/packages. It also fails to load local modules referenced by absolute path or with the extension '.coffee'. The last is totally acceptable and the requirement for relative paths is manageable if necessary but obviously the failure to load meteor packages is a show stopper.

Sample project demonstrating the issue: https://github.com/jdrucza/coffee-wallaby

NOTE: I renamed the wallaby_server.js to '.wallaby_server.js' (i.e. starting with a '.') so it would not be picked up by meteor.

Probably obvious but for completeness/search-aid the error is:

wallaby.js started
core v1.0.259
Runtime error: Error: Cannot find module 'meteor/meteor'
    at Function.Module._resolveFilename (module.js:338:15)
    at Function.Module._load (module.js:280:25)
    at Module.require (module.js:364:17)
    at require (module.js:380:17)
    at Object.<anonymous> (./imports/testSetup.spec.js:5:26)
....

I suspect the issue may be that wallaby needs to be configured to use meteor's coffeescript rather than its in-built version but I've no idea how to make this happen.

ghost commented 7 years ago

This is indeed a limitation of the current implementation.

I think a reasonable solution would be to make the Node.js require aware of the Meteor packages by adding them to require.cache.

E.g.

const meteorRequire = meteorInstall();
require.cache['meteor/meteor'] = meteorRequire('meteor/meteor');
jdrucza commented 7 years ago

Seemed like a good suggestion except it doesn't work because the cache key is the resolved filename not the string identifying the module. So I tried to replace require.resolve with a function that would resolve anything starting with 'meteor' to the module name and pass everything else through but it seems that require doesn't actually use require.resolve, rather they both use Function.Module._resolveFilename but I haven't yet found a way to override this... any further suggestions?

At the moment I am going to try overriding or replacing 'require' with one that uses meteorRequire directly when possible.

ghost commented 7 years ago

Another approach would be to use proxyquire in the test files:

const meteorRequire = meteorInstall();
const proxyquire = require('proxyquire');

const stubs = {
  // meteor/meteor is not available in our testing environment, so we need `'@noCallThru': true`
  '@noCallThru': true,
  'meteor/meteor': meteorRequire('meteor/meteor')
};

proxyquire('./unit_under_test', stubs);

And yet another approach maybe is to load the app files that were compiled by the Meteor build tool by removing https://github.com/xolvio/automated-testing-best-practices/blob/master/wallaby_server.js#L235-L238 and using meteorRequire in your tests.

const meteorRequire = meteorInstall();
const unitUnderTest = meteorRequire('./unit_under_test');
jdrucza commented 7 years ago

Thanks for the suggestions. I ended up going with a solution that replaced the use of require for all non-relative-path require statements with a function from an imported module at the root of the app that conditionally used meteorRequire, not ideal but solved a couple of require related challenges and is a fairly easy search and replace refactor if and when possible.

This worked well until trying to execute tests that involved running code from the meteor packages (the primary goal) which resulted in: "before all" hook: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment. because the before all contained resetDatabase() from xolvio cleaner. I tried the suggestion but then the test itself reported the same fiber related issue.

Is it correct and necessary to call Meteor.bindEnvironment for every describe / it / before block and if so will this also work when running under meteor test? Am I missing something else, like is there a way/need to tell wallaby to use the dispatch:mocha-phantomjs installation rather than its own mocha?