Using Gagarin’s DDP Client to test Meteor methods, publications and subscriptions

On my previous post, we briefly went over unit testing in Meteor and Mantra by using Sinon’s spy and stub. We have discussed the difference between the two functions and determined when to use them for our unit tests.

Today, we are going to go through basic integration testing with methods and publications/subscriptions using Gagarin’s DDP client. Gagarin is the Mantra spec’s recommended testing framework for doing integration testing. It is versatile and can do a lot more that what we are going to cover here, like using chromedriver and Selenium for end to end testing, for example.

About Gagarin

According to the project’s documentation,

Gagarin is a mocha based testing framework designed to be used with Meteor. It can spawn multiple instances of your meteor application and run the tests by executing commands on both server and client in realtime. In other words, Gagarin allows you to automate everything you could achieve manually with meteor shell, browser console and a lot of free time. There’s no magic. It just works.

Gagarin is based on Laika, another testing framework created by Arunoda. According to the documentation, it can be thought of as Laika 2.0, though it is not backward compatible. The main differences between Gagarin, Laika and Velocity can also be found on the documentation above.

Installation

We can simply install Gagarin by running

npm install -g gagarin

Once we have written some tests, we can just run this command at the root of our app directory:

gagarin

By default, it will look for files that are in the tests/gagarin/ directory. It will build our app inside .gagarin/local/ along with the database that it will use for the duration of the test, which can be found at .gagarin/db/.
Now that we have a basic understanding of how to install and run Gagarin, let’s proceed and look at the code snippets that we are going to test.

Meteor code snippets (for testing)

In order for us to understand what we are testing, the code snippets will be included here so we can easily reference the functions and how they are being tested. I have simplified these code snippets so that we can focus more on testing.

Collection

const Categories = new Mongo.Collection(‘categories’);

Publications

Meteor.publish(‘categoriesList’, () => {
  return Categories.find()
});
Meteor.publish(‘categoriesOwnedBy’, (owner) => {
  check(owner, String);
  
  return Categories.find({owner: owner});
});

Methods

Meteor.methods({
  ‘categoriesAdd’(data) {
     check(data, Object);
 
     Categories.insert({
       name : data.name,
       owner: data.owner,
       createdAt : new Date(),
     });
   },
   ‘categoriesUpdate’(data) {
     check(data, Object);
     
     Categories.update({_id:data._id},{$set:{
       name : data.name, 
       owner: data.owner,
       modifiedAt : new Date(),
     }});
   },
});

Writing Gagarin Tests

Now that we have seen the code that we are going to test, we can now start writing basic tests in a single JavaScript file inside tests/gagarin/. Because Gagarin is based on Mocha, it has the same describe — it structure. Chai’s expect is also exposed for doing more semantic assertions.

Testing the categoriesAdd method

The test we are going to do first is to check whether or not we can add something to the categories collection.

describe(‘Categories’, function() {
  var app = meteor({flavor: “fiber”});
  var client = ddp(app, {flavor: “fiber”});
  it(‘should be able to add’, function() {
    client.call(‘categoriesAdd’, [{name: ‘First category’}]);
    client.sleep(200);
    client.subscribe(‘categoriesList’);
    var categories = client.collection(“categories”);
    expect(Object.keys(categories).length).to.equal(1);
 }); 
}

We are defining the initial describe block that we are going to use for this example. Gagarin gives us two useful global functions that are essential for running tests: meteor and ddp.

meteor is used to spawn a new Meteor instance that we have assigned to the app variable. Meteor uses fibers by default, so we need to specify it as the flavor. ddp allows a client to connect to the Meteor instance that we have just created by passing the reference of the instance and the flavor as its arguments.

Since we now have our Meteor app and our client configured, we are ready to proceed with our first test case: making sure that we can successfully add a new category.

Inside our it block, we are calling the Meteor method categoriesAdd. Gagarin provides our client with a handy call function that works exactly the same way as Meteor.call. The only difference is that the arguments need to be inside an array, regardless of its number.

We then use the sleep function to add a little delay so that we can make sure that the new document comes to the client. We are subscribing to our categoriesList publication through the handy subscribe function of our client. Just like the call function, this is similar to Meteor.subscribe, which makes it very straightforward.

After subscribing to our publication, we now check if the document has been inserted by our Meteor method to the collection. We do that by calling the collection function of our client, passing the name of the MongoDB collection as an argument. It returns an object that looks like this:

{ Hpu6Z4h7ZFtC6Q77m: 
 { _id: ‘Hpu6Z4h7ZFtC6Q77m’,
 name: ‘First category’,
 owner: null,
 modifiedAt: 2016–06–07T08:29:06.026Z,
 createdAt: 2016–06–07T08:29:06.026Z } }

It looks similar to something that we would get if we query our collection using find, aside from the fact that instead of getting back an array or a cursor, we are getting an object which has the _id field as a key.

We then use Chai’s expect function to do a simple assertion and that completes our first test. Object.keys has been used on the object that was returned by the collection function, so we can just expect the resulting array to have a length of 1. This test makes us sure that the client can call our method, and can receive the the document through our publication.

Testing the categoriesUpdate method

We now have a basic test that checks if we can insert and retrieve documents, what we want to do next is to check if we can update a certain category from our collection. The process is similar to what we did on the last section (still goes inside the same describe block):

it(‘should be able to update’, function() {
  client.subscribe(‘categoriesList’);
  var categories = client.collection(“categories”);
  var id = Object.keys(categories)[0];
  client.call(‘categoriesUpdate’, [{_id:id, name: ‘updated    category’}]);
 client.sleep(200);
 
  categories = client.collection(“categories”);
  expect(categories[id].name).to.equal(‘updated category’);
});

The only thing that is new here is that we are storing the id of the category that we want to update so we can use it when we call categoriesUpdate. We can then check if the name has been updated by using expect.

Testing categoriesOwnedBy publication

The next thing that we will test is the categoriesOwnedBy publication. Since we did not use the owner field in our previous examples, we will put this test on a separate describe block. That will allow us to spawn a new Meteor and database instances that has nothing to do with the previous one.

describe(‘categoriesOwnedBy publication’, function() {
  var app = meteor({flavor: “fiber”});
  var client = ddp(app, {flavor: “fiber”});
  it(‘should only publish a specific users category’, function() {
    app.execute(function() {
      var categories = [
        { name: ‘Category 1’, owner: ‘John’ },
        { name: ‘Category 2’, owner: ‘Jessica’ },
        { name: ‘Category 3’, owner: ‘John’ }
      ];
      Meteor.call(‘categoriesAdd’, categories[0]);
      Meteor.call(‘categoriesAdd’, categories[1]);
      Meteor.call(‘categoriesAdd’, categories[2]);
   });
  client.subscribe(‘categoriesOwnedBy’, [‘John’]);
  
  var johnsCategories = client.collection(‘categories’);
  
  expect(Object.keys(johnsCategories).length).to.equal(2);
});

This looks similar to our two previous examples, but this time I am using the execute function of our Meteor instance. It accepts a callback function as an argument, and the contents of that function will be executed in the server context. Notice how we have access to Meteor.call inside this function?

We then go back to the client context and subscribe to our categoriesOwnedBypublication, passing ‘John’ as our argument. After fetching the contents of the collection, we are checking if we are getting the expected number of documents that was published by our collection.

Running our test

If we run gagarin on the root folder of our application, we will get something similar to this:

Conclusion

Using the examples above, we have seen how to create simple integration tests using Gagarin on Meteor. These test cases might seem contrived, but the idea here is to get an overview of how to use Gagarin’s DDP client to perform basic integration tests that deals with Meteor methods, publications and subscriptions.