How do you test a packages-for-everything app?

Yeah, of course packages like these require that you write your code with some other principles in mind than “normally”. Although I have to say, one really important aspect for me was that Space is not as invasive as e.g: React.

Basically you can write all of your app code in plain Javascript without any reference to the Space framework. You only have to add a Dependencies object your classes/prototypes (think: annotation) which tells the injector what you require. The cool thing is, you don’t need Space at all to test your business logic classes – you can simply provide the dependencies yourself during the tests, possibly some stubs or simplified implementations.

The Space modules / app are just a convenient sugar on top of the basic building blocks. You could also just use the Space.Injector with a completely custom way how your app is structured and initialized. And hey, if you come up with better solutions for some of the implementations, drop me a line – i am always happy to improve Space :wink:

Regarding bugs and issues – you wouldn’t be the first one to use Space for a production application and I am working on two big apps completely built with these tools every day. So this package (and the many others built on top of it) won’t die any time soon and in fact i am constantly working to improve the details. Of course, everything is completely unit tested and written in a really simple style, so that bugs have a pretty hard time in the repo :stuck_out_tongue:

1 Like

@CodeAdventure that is the most meta package I’ve ever seen. I had to re-read it at least three times to understand what it was doing. You’ve made a framework inside of a framework. On top of Meteor’s namespacing, inheritance, and dependency system, on top of JavaScript’s namespacing, inheritance, and dependency system, you’ve gone and added another layer of logic to understand and work within just to organize, reference, and reuse your code.

I don’t intend to be rude, but my honest opinion is that it seems like one huge anti-pattern to me. Instead of being more rigorous about your dependency injection, only having one Meteor.startup in your application, using proper stubs and function spies for testing, you made a system that’s enabling your bad habits but saving your codebase from total mayhem.

To each his own, but geez is that a wacky package.

To @chmac’s original question — in our applications, any code that’s supposed to run on startup is exported through the package and invoked in the application’s Meteor.startup. We don’t put any startup calls in packages because it’s very obfuscated then as to what’s actually happening when your application is running.

One of the cool things about Meteor’s packaging system is that the package manifests are dynamic (.js), rather than static (.json). So, like you mentioned earlier, you can conditionally exports symbols when testing. Here’s a relevant snippet from one of our packages:

  // packages/grove-receiver/package.js
  if ( process.env.TESTING_FLAG === '1' ) {
    // In testing environment export these so they can be
    // stubbed/observed appropriately
    api.export([
      'ParseEvent',
      'Receiver',
      'Grove',
      'Groves'
    ], 'server');
  }

  api.export([
    'OpenGroveStream',
    'Spark',
  ], 'server');

This is from an application that has nothing in it except for the Meteor.startup on the server. This is literally the only file, the entire application:

/// server/main.js
Meteor.startup(() => {
  Spark.login({
    username: process.env.SPARK_USERNAME,
    password: process.env.SPARK_PASSWORD
  })
  .then( Meteor.bindEnvironment(function(token) {
    console.log('Logged into Spark:', token);
    OpenGroveStream();
  }))
  .catch(function(err) {
    console.error('Login to Spark failed:', err);
    Kadira.trackError('spark-login-error', err);
  });
});

We also maintain a package.json file for each of our applications so we can run tests much more easily. The startMongo and stopMongo stuff is because there’s a separate database instance running in addition to the default one (needed when doing high-velocity database updates). With this, running tests are as simple as saying npm test. Or if you want to the nice HTML reporter with tests automatically re-running for a specific package, npm run pretty-receiver-tests.

// grove-cloud package.json
{
  "name": "grove-cloud",
  "version": "0.1.0",
  "description": "Meteor application for receiving published events from Grove hardware",
  "scripts": {
    "start" : "npm run startMongo && source config/development/env.sh && meteor",

    "stop": "npm run killMongo",

    "test" : "npm run receiver-tests",

    "receiver-tests" : "npm run startMongo && source packages/grove-receiver/tests/env.sh && meteor test-packages --velocity grove:grove-receiver && npm run killMongo",
    "pretty-receiver-tests" : "npm run startMongo && source packages/grove-receiver/tests/env.sh && meteor test-packages --driver-package respondly:test-reporter grove:grove-receiver && npm run killMongo",

    "startMongo": "mongod --smallfiles --dbpath .db --fork --logpath /dev/null",
    "killMongo": "mongo admin --eval 'db.shutdownServer()'"
  },
}

1 Like

Hey @louis! Yep you got it: space:base is a meta package and tries to fill a gap that many struggle with when starting with Meteor: how to structure your app and manage dependencies. To be honest, you get the biggest benefit from the module/app system of Space when you don’t split up your app into sub-packages like in this basic TodoMVC example. Because most people run into load-order issues pretty soon.

You’re right, if you start with a package-only structure from the beginning you probably won’t need the DI stuff if that’s not your cup of tea. However, I don’t see a lot of “bad habits” there, it merely makes patterns explicit instead of the implicit nature of many other packages. Think about it: a lot of packages out there basically boil down to a static singleton pattern. Even Meteor uses it for most of its APIs and in my opinion i like the explicit approach more: Define your classes like normal and make them singletons during runtime. This way you can interact with them in your tests without having to stub your whole environment.

Thanks for the critical input – I will think about making the DI system more optional, so that you can use all the features of the various space packages without using the app/module system.

Sidenote:
May I ask you why you use Promises on the server? For me the biggest feature of Meteor is that it uses fibers under the hood and I can treat async code like synchronous code.

1 Like

@louis Awesome. Thanks a lot. That’s exactly what I was looking for. Practical insights into how other folks are solving this problem. I like your approach a lot. I particularly like the package.json idea, I hadn’t thought about including that.

Presumably your env.sh file sets TESTING_FLAG='1'. I was a bit concerned about that flag being missed when we run tests locally, but adding it into package.json is an elegant solution.

There is also a testOnly flag on api.export() which can export values only when that package itself is under test. It looks like api.export('Foo', ['server'], {testOnly: true});. I don’t think it’s very widely documented, but I’ve personally tested it, and it’s working for us.

I’m leaning towards the approach of exporting startup functions from all my packages, wrapping them in a startup function inside my app package (which I can test) and then having a single line in my application itself that just calls app's startup function. Then almost everything is testable, and it requires no external dependencies.

PS> Grove looks like an awesome project, you guys need to launch in Berlin!

3 Likes

No problem, you’re welcome. I had NO idea that testOnly flag existed, thanks! Oh documentation… :sweat_smile:

And thanks for the note about Grove! :blush: I can’t wait till we can get them all around the world; hopefully the app will be much better by the time we reach Germany :laughing:

Hey @CodeAdventure! Ok, I understand your package a bit better now. So in the applications that you use Space on, do you eschew Meteor packages and make your own with space:base?

Can’t wait for official module support, gonna be way mo’ betta’. Less magic about where stuff is coming from, way easier to look at a single file and reason about what’s happening.

I use Promises on the server because it’s a clean syntax and now a core part of Javascript. Fibers are a really cool invention, but too hard to explain and understand for a lot of developers. MDG is actually replacing all of the Futures in Core with Promises automatically wrapped by a Fiber. You’d probably like this thread -> Fibers and meteor-promise npm pacakge

1 Like