I spent some time playing with getting module-based unit testing up and running with the new 1.3 beta release. I wrote up a post outlining how to use plain Mocha to write fast, mocked unit tests.
Cool, great initiative. I’ve waited for real modules in Meteor, so we can throw in any test runner.
An open musing I have is how mocking/stubbing vs. Dependency Injection is going to work with Meteor’s application testing. I.e. what’s the preferred coding style, as it’ll affect the code under test. I noticed this.Meteor = Meteor in the post example, and while I think it’s clean according to pure Software Engineering practices, there’s a lot of Meteor code out there which would have to convert to DI.
There are a lot of globals in the standard Meteor API. Add global collections on top of that, and there will be a lot of dependency injection to do, which surely will create more verbose app code (imo).
I totally agree. The verbosity of explicit dependency injection is definitely a downside of going this route. But then again, it might help to see exactly what your dependencies are and how your module is using them.
If that kind of explicit dependency handling turns you off, you could still rely on the normal, global Meteor objects, if your load order allows it. You might have to get a little fancy with your unit testing to mock out all of those implied globals though. And in that case, it would be a lot harder to know exactly what needs to be mocked for each test.
I guess time will tell what the best pattern will be.
I’ve been doing a similar thing recently in 1.2 for testing individual coffeescript ‘classes’ with Jasmine. This also lets you have fast running tests that don’t require Meteor to be running:
# myclass.coffee
@MyClass = class MyClass
constructor: ->
# blah blah blah...
# module is undefined in Meteor so this will be skipped except for when we're running
# tests from the command line with jasmine. Install with: npm install -g jasmine
module?.exports = MyClass
Now in my jasmine tests I can just* do:
# myclass_spec.coffee
MyClass = require('./../blah/myclass')
describe 'sometest', ->
it 'does something', ->
myClass = new MyClass
expect(myClass.something).toBe(true)
But as @johanbrook says, because of all the Meteor globals any older code is going to have to be refactored to use some form of DI so we can create mocks.
(*some setup required to get jasmine to run coffeescript tests)