How to test meteor methods?


#1

I’m using
practicalmeteor:mocha
I run the test server with
meteor test --driver-package practicalmeteor:mocha --port 3100

My test is this

import { Meteor } from 'meteor/meteor';
import { chai, assert } from 'meteor/practicalmeteor:chai';

describe('Accounts', () => {

	describe('accounts.create', () => {
		it('Can create a user ', () => {
			Meteor.call('accounts.create','demo', 'demo@demo.com', 'demopassword', {})
			const newUser = Meteor.users.findOne();
			assert.isObject(newUser,'user object was inserted');
			assert.deepProperty(newUser,'profile.name','user has a name');
		});
	});
});

And throws this error

Error: Method 'accounts.create' not found [404] at Server.apply (packages/ddp-server/livedata_server.js:1589:19) at Server.call (packages/ddp-server/livedata_server.js:1559:17) at Context.<anonymous> (server/methods/accounts.test.js:11:11) at run (packages/practicalmeteor:mocha/meteor/src/setup/SetupRunnables.js:113:18)

I don’t know why my method is not found


#2

Hi,

Did you find a way to test the user creation ?

Regards.


#3

You could use something like the following to (integration) test user creation (note: is leveraging Mocha’s Promise support instead of using an async done callback):

import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import { chai, expect } from 'meteor/practicalmeteor:chai';
import { resetDatabase } from 'meteor/xolvio:cleaner';

describe('Accounts', function () {

  beforeEach(function () {
    resetDatabase();
  });

  it('should be able to create a user', function () {
    const createUser = new Promise((resolve, reject) => {
      Accounts.createUser({
        username: 'demo',
        email: 'demo@demo.com',
        password: 'demopassword',
      }, (error) => {
        if (error) {
          reject(error);
        } else {
          const newUser = Meteor.users.findOne();
          resolve(newUser);
        }
      });
    });
    return createUser.then(function (newUser) {
      expect(newUser).to.not.be.undefined;
      expect(newUser.username).to.equal('demo');
    });
  });

});

#4

Hi @santypk4,

With meteor testing you either do unit test or full app tests.

Unit tests

When you do a unit test, you need to include all the necessary code. With the test flags you chose you are initiating a unit test. To get it to work you will need to include the file that has the method definition in it. See the simple integration test section of the guide notice that it includes the todos and the lists collection definition. In essence the test is completely encapsulated and does not depend upon the rest of the app. I think you may run into trouble trying to make meteor method calls, but the mongo collection calls should work fine.

Full app tests

You may be actually trying to do a full-app type of acceptance test. Full app tests run all of your code. If that is the case you will need to use the --full-app flag. And you will need to write some server code hooks to make all of those server calls, see chimp’s meteor specific features.

Note: the server global variable is only accessible within a mocha test scope & before / after hook scopes and not available within the describe scope.

The meteor testing article is very good. But it is very dense! There are a lot of topics covered in that section.

Hope this helps.


#5

Hey, I used the same approach however I found that assertions inside then callback fail silently. e.g if I modified the code above to:

 return createUser.then(function (newUser) {
      console.log('BEFORE FAIL')
      expect(1).to.equal(2);
      console.log('AFTER FAIL');
      expect(newUser.username).to.equal('demo');
    });

as a result, tests are all success which is wrong and the output in the console is BEFORE FAIL so it failed after assertion error, but no error is logged anywhere.
This is serious problem, because tests might appear false positives.
My package versions:

practicalmeteor:chai@2.1.0_1
practicalmeteor:loglevel@1.2.0_2
practicalmeteor:mocha@2.4.5_2
practicalmeteor:mocha-core@0.1.4
practicalmeteor:sinon@1.14.1_2

#6

Strange - it should work; can you share your full test case including where you’ve defined your Promise?


#7

Here is a test case I use to reproduce it:

    it('should fail on async method wrong assertion', function () {
      const promise = new Promise((resolve, reject) => {
        HTTP.get('http://google.com', (error, result) => {
          if (error) {
            reject(error);
          } else {
            resolve(result);
          }
        });
      });
      return promise.then(function (result) {
        console.log('result',result.statusCode);
        expect(1).to.equal(2);
        console.log('this message is not printed!');
      });
    });

The only what get’s printed is result 200 and test passes.
I run tests with command meteor test --full-app --driver-package practicalmeteor:mocha --settings settings-test.json


#8

Hmm - might be a bug with practicalmeteor:mocha. If you change your expect line to:

try {
  expect(1).to.equal(2);
} catch (err) {
  console.log(err);
}

You’ll see the assertion error is firing properly, it’s just being swallowed:

{ [AssertionError: expected 1 to equal 2]
message: 'expected 1 to equal 2',
showDiff: true,
actual: 1,
expected: 2 }

This might be related to:

For now you might want to work around this by switching back to using Mocha done syntax for this test.


#9

Thanks for your help, however I’m still struggling to fail the tests.
If I use done() like here:

it('should fail on async method wrong assertion', function () {
    HTTP.get('http://google.com', (error, result) => {
      expect(error).to.not.exist;
      expect(1).to.equal(2);
      done()
    });
});

I end up with this in the console

(STDERR) MochaRunner.runServerTests: failures: 0
Exception in callback of async function: AssertionError: expected 1 to equal 2

So far no luck to test results from async call. Am I the only one in the entire Meteor community with this problem?


How to write negative test cases in meteor
#10

Make sure you pass done into your test function:

it('should fail on async method wrong assertion', function (done) {
...

That will give you the proper failure in the console, but not via the web reporter (which is tied to the issue I linked to - https://github.com/practicalmeteor/meteor-mocha/issues/11). So until that gets fully resolved you could wrap your expect calls in a try/catch, and pass any captured error into done. Ugly, but it will work for now:

try {
  expect(1).to.equal(2);
} catch (err) {
  done(err);
}
done();

#11

Thank you. That solves my problem. I checked and web reporter does indeed report the error. That’s great.


#12

try/catch worked for me too. Here is the short example if method under test returns promise:

function methodWhichReturnsPromise() {
  return Promise.resolve(false);
};

it('should fail in reporter', function (done) {
  methodWhichReturnsPromise.then(function (result) {
    assert.isTrue(result); //should fail;
    done();
  }).catch(function (err) {
    done(err);
  });
});

#13

I think you may run into trouble trying to make meteor method calls, but the mongo collection calls should work fine.

What was the problem of calling server methods? Is there a way to call server methods from mocha chai unit testing? Please kindly advise


#14

As long as we’re focusing on unit tests, @tmeasday made use of a Meteor internal object to do a method unit test here:


Calling methods from test, but not logged in
#15

Here are samples how I test methods with mocha, chai etc.

Hope this helps.