Unit testing with Geddy says geddy is not defined #337

Closed
elayman opened this Issue Mar 23, 2013 · 27 comments

Projects

None yet

4 participants

@elayman

I'm running Geddy MVC Framework on Node.js on Heroku right now with a Postgres database. I can't seem to find any unit test frameworks for node.js that support Geddy. I have tried nodeunit, jasmine and a couple others but none seem to work with geddy (saying it is undefined - I think because it doesn't know how to import all of the required files).

I know Geddy provides their jake test tool but it doesn't seem to work either. It gives me this output:

ReferenceError: geddy is not defined

Any help would be greatly appreciated. Also looking for a code coverage tool that will work with the unit testing framework I get working.

Thanks!

EDIT: My tests all start with this header to import the required modules:

var assert = require('assert')
  , tests
  , Activity = geddy.model.Activity;

And the import of Activity fails with the ReferenceError above, so it never even tries my tests.

Here is a nodeunit test I wrote

var tests
  , User = geddy.model.User;

exports['addUser1'] = function (test) {
    var user = User.create({username: 'Greg',
                        password: 'MyPassword!',
                        confirmPassword: 'MyPassword!',
                        familyName: 'LastName1',
                        givenName: 'FirstName1',
                        email: 'Greg@greg.com'});
User.add(user, function (answerDict) {
    test.equal(answerDict, {'errCode': 1});
    test.done();
});
};

And the output

...../test/test-user.js:2
  , User = geddy.model.User;
           ^
ReferenceError: geddy is not defined
@MiguelMadero

Can you try running geddy jake test?
I have not done it, but you could also add a jake
task
to run your tests using a different
test runner.
It would look something like

task('customTests', {async: true}, function () {
  var cmds = [
    'nodeunit ./tests/'
  ];
  jake.exec(cmds, function () {
    console.log('All tests passed.');
    complete();
  }, {printStdout: true});
});

Then run geddy jake test. Geddy jake tests calls another task (env:init)
as a pre-req and that's why you get all of those undefined errors.

I played with node-jscoverage a
while ago, not with geddy, but I think it could work. Clone the repo
instead of using the one from npm.

Let me know if this works.

Miguel

@elayman

So I tried your suggestion but unfortunately, I get the same issue:

...../test/test-user.js:2
  , User = geddy.model.User;
           ^
ReferenceError: geddy is not defined
@MiguelMadero

I'm not sure the error is related to how we run the tests. Does it work from the website?

Can you try the following:

geddy app newapp
cd newapp
geddy scaffold activity title other
geddy jake test

The scaffold generator will add a test for you that references geddy.model, that one will register your activity model. Can you try this just to discard the issue is at this layer.

@elayman

Miguel, I tried your suggestion and it works.

Okay I think I got jake tests to run now. I'm not sure what I did... but I basically made a new folder called test3 and put my tests in there and changed the directory from the JakeFile to point to it. I notice the tests just run forever though (ie. I have to hit CTRL+C in order to stop the process), but it doesn't do this with the simple example test.

I get the correct output with all of my tests:

Test UserModel Add
Test Model Add Same
Test Model Add Different
Test Model Add Empty Username
Test Model Add Null Username
Test Model Add 129 Username
Test Model Add Empty Password
Test Model Add Null Password
Test Model Add 129 Password
Test Model Login Bad Credentials
All tests ran successfully

but it just sits there at the end.

@MiguelMadero
@elayman

I tried the node-unit jake task again and I get this output (not sure why the n's are removed):

/usr/local/lib/node_modules/geddy/node_modules/jake/lib/api.js:225
        throw errObj;
              ^
ode_modules
odeunit/lib
odeunit.js:75:37
    at _concat (/usr/local/lib
ode_modules
odeunit/deps/async.js:508:13)
    at async.forEachSeries.iterate (/usr/local/lib
ode_modules
odeunit/deps/async.js:118:13)
@elayman

Ah! I forgot complete();

Thanks for your help. Any ideas on my nodeunit error?

@MiguelMadero
@elayman

Yes, I run: nodeunit test3/userTests.js

.../test3/userTests.js:3
  , User = geddy.model.User
           ^
ReferenceError: geddy is not defined
@MiguelMadero
@mde
@elayman

I apologize for my noobiness. I am new to node.js and Geddy and therefore nodeunit as well. I tried to add the env:init to the cmds from your example Miguel, but it says command not found

task('default', {async: true}, function (params) {
  var cmds = [
    'env:init',
    'nodeunit test/test-user.js'
  ];
  jake.exec(cmds, function () {
    console.log('All tests passed.');
    complete();
  }, {printStdout: true});
});

Error: /bin/sh: env:init: command not found

@MiguelMadero

@mde is right. Nodeunit is using a different process, so this solution won't work. Even if we initialized the env in jake, we're starting a new process, hence new memory, new domain and undefined variables.

Now, just as a reference, if we wanted to initialized the environment before we would run it as a jake task, not as another command. I was on the phone and couldn't send a link, but you can have a look at the jake docs for for further reference. To execute another jake task would run jake.Task['env:init'].invoke();. Keep in mind that this won't work since running nodeunit creates a new process. Also consider this shouldn't be needed if you run geddy jake, since geddy will do exactly this, run env:init before any other jake task.

Now a couple of solutions are:

  • Call env:init from nodeunit. You could either require jake, point it to the geddy Jakefile and call require init. Or, just use the code from the env:init task. Note: we should probably refactor this so it can be used easily outside of jake by just calling something require('geddy').init()
    var cfg = {};
    if (process.env.environment) {
      cfg.environment = process.env.environment;
    }

    jake.addListener('complete', function (e) {
      jake.Task['env:cleanup'].invoke();
    });

    geddy.config = require('../lib/config').readConfig(cfg);
    geddy.model = require('model');

    require('../lib/init').init(geddy, function () {
      complete();
    });

Have a look at moduleStart on nodeunit. Looks like that would be the place to initialize the geddy environment.

  • Call nodeunit inside the jake process, instead of creating a new process. You can create a new task in jake to run nodeunit tests. Inside instead of using the execute command as I suggested earlier, require nodeunit as described on the nodeunit docs:
var reporter = require('nodeunit').reporters.default;
reporter.run(['test']);

I've not had a chance to test any of this, but I hope this gives you some pointers. Let me know how it goes. We will have to do some changes to geddy to make this easier and add some docs. It's always good to play nice with alternative options, and jake isn't really shining as a test runner so this is an important category.

@elayman

So I tried to mess around with this today but couldn't get it working. It seems running the reporter.run still initializes a new process because the behavior is the same as calling exec nodeunit.

EDIT: I put the geddy init code in nodeunit/lib/reporters/default.js in the moduleStart method. Unfortunately, it must load the test file before it runs the moduleStart, so I had to create a new test file (with name earlier in the alphabet so it gets loaded first) with a fake test in it because my references to Geddy in my test file crashed the process. I also had to change some of the relative locations to access the geddy module. However, it seems to get to /geddy/lib/init/model.js:55:20 and errors because the require('utilities') doesn't seem to work correctly TypeError: Object #<Object> has no method '_getConstructorsFromDirectory'

EDIT2: I was able to fix that error by adding geddy.utils = require('../../../geddy/lib/utils'); to my init code. But now It says Error: Model Activity did not get registered properly. from geddy/lib/init/model.js:70:15

And unfortunately, the comment above this line doesn't help me much 😃 // If the model doesn't exist, something is fucked up

geddy = require('../../../geddy/lib/geddy');
            var cfg = {};
            if (process.env.environment) {
              cfg.environment = process.env.environment;
            }

            jake.addListener('complete', function (e) {
              jake.Task['env:cleanup'].invoke();
            });
            geddy.utils = require('../../../geddy/lib/utils');
            geddy.config = require('../../../geddy/lib/config').readConfig(cfg);
            geddy.model = require('../../../geddy/lib/model');

            require('../../../geddy/lib/init').init(geddy, function () {
              complete();
            });
@MiguelMadero
@elayman

I am not opposed to another testing framework, however I believe I will have similar issues with others won't I? Do you know of a test runner that doesn't use another process?

@MiguelMadero
@elayman

Thanks for all of your guidance @MiguelMadero. I found a jakefile for running Mocha here: https://gist.github.com/BryanDonovan/5022389

I will try to use Mocha since it seems to be another good testing framework.

@MiguelMadero
@elayman

Done: https://github.com/mde/geddy/wiki/Unit-Testing-With-Geddy

Feel free to edit or mention things I should add!

@mde

Fantastic! I know this will help a lot of people out.

@elayman

Since this is somewhat related (I had mentioned I wanted to use a code coverage tool), do you know if I can do something similar to this post to run node-jscoverage, (ie. flatten the app directory and use instinct, or will Geddy complain)?

"add instinct.js that consolidated all my models, controllers, middleware and exported them from one place. Then elsewhere in my project anytime I needed a model, controller or middleware I just required that main instinct module like so:

var instinct = require('./instinct');

Then I added an index.js file at the project root that exports either lib/instinct or lib-cov/instinct depending on the INSTINCT_COV environment variable (which we'll set in our MakeFile)"

Original link: http://brianstoner.com/blog/testing-in-nodejs-with-mocha/

@elayman

@MiguelMadero - do you know if there's a config in geddy where it chooses the app/ directory so I can change it to app-cov/ when I'm using jscoverage?

@larzconwell

@elayman No there isn't, although you could just symlink app-cov to app.

@mde

There is an option to set the app directory when running the server (-g/--geddy-root), but it only works when running the server. (geddy jake just does passthrough to Jake, so most of the CLI options don't actually do anything.)

Another alternative would be to have a Jake task that you use a a prerequisite that does process.cwd to a desired directory before running your tests.

@elayman

I followed @mde 's advice and created a copy of the directory before covering the code. Then I change directory and run the tests.

Here is my wiki with link to my code: https://github.com/mde/geddy/wiki/Unit-Testing-&-Code-Coverage-With-Geddy

@elayman elayman closed this Apr 26, 2013
@mde

Nice. Thanks for the wiki link. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment