I apply the QUnit to my Emberjs project to do unit/integration testing.
In unit testing we usually have to mock object/methods to test every condition.
I choose SinonJS as my mock library. It’s really easy to use.
Let’s see how to unit testing my components.
Testing html
Create a html that includes qunit,sinon,emberjs and your source codes. Remeber to use debug Emberjs(ember-1.x.x.js) not production(ember-1.x.x.min.js)
Make sure you invoke setupForTesting and injectTestHelpers after Application created.
In QUnit we could use module() to group test cases. test() after module() will be grouped.
Then we could add setup and teardown methods. They will be invoked in each test case.
lobby.spec.js
123456789101112131415161718
module("LobbyRoute",{setup:function(){MyApp.reset();},teardown:function(){//do something here.}});test("should render lobby template",function(){Ember.run(this,function(){route=MyApp.__container__.lookup('route:lobby');sinon.spy(route,"render");route.renderTemplate();ok(route.render.calledOnce,"render should be calledOnce");});});
Above example uses Ember’s __container__.lookup to find specific component and testing it.
But how we make sure the specified method is called?
Use sinon.spy to spy a object/method and check it.
Controller
I have a backward function and it will invoke exitApp when isWebview() is true.
In below example, we stub the ViewManager’s isWebview method.
I want it returns true during testing. Here is a little difference from previous example.
In the last two lines. I have to invoke restore in each stub/spy method.
Why? The same method can’t be spy/stub twice. We have to restore it to prevent other test cases be influenced.
Why previous example doesn’t have restore in route? Because lobby route is in Ember container and it will be re-created in MyApp.reset()
lobby.spec.js
1234567891011121314
test("backward should invoke exitApp in webview",function(){Ember.run(this,function(){controller=MyApp.__container__.lookup('controller:lobby');sinon.stub(ViewManager,"isWebview").returns(true);sinon.spy(LifeManager,"exitApp");controller.backward();//assertequal(LifeManager.exitApp.callCount,1,"LifeManager.exitApp should be called");ViewManager.isWebview.restore();LifeManager.exitApp.restore();});});
In setup we have to append view to document.
Then we could use css selector to find expected element.
Remeber to remove it from document in teardown.
lobby.spec.js
123456789101112131415161718
module("LobbyView",{setup:function(){Gamenow.reset();Ember.run(this,function(){this.view=MyApp.__container__.lookup('view:lobby');this.view.append();// Hook up to our document.});},teardown:function(){Ember.run(this,function(){this.view.remove();// Unhook from our document.});}});test("header should has logo",function(){equal(this.view.$(".lobby-header .lobby-logo").length,1,".lobby-logo should exist");});