Ember Testing

Testing in Ember

After all the features are build and all the code is written, there would be these Tests that would be missing, which points us wonder how effective our code is. This is when you wished you had Tests. Maybe it’s not too late, you can start beefing up your Test Coverage. Here are few basics to get you started on Testing using Ember, Sinon and Qunit.

Using Sinon with Qunit

https://github.com/csantero/ember-sinon

Installation

npm install --save-dev ember-sinon
ember g ember-sinon

Sinon

http://sinonjs.org/docs/

Sinon provides you with following helpers for your testing needs.

  • Spies
  • Stubs
  • Mock
  • Asserts

Spies

A test spy is a function that records arguments, return value, the value of this and exception thrown (if any) for all its calls. A test spy can be an anonymous function or it can wrap an existing function.

When to use spies?

Test spies are useful to test both callbacks and how certain functions/methods are used throughout the system under test. The following simplified example shows how to use spies to test how a function handles a callback:

Example,

//Setting Spies
var spy0 = sinon.spy();

//Spy can spy on existing function
var spy1 = sinon.spy(controller, "method1");

//Trigger
controller.method2("1");

//Verify
assert.ok(spy1.calledOnce);
assert.ok(spy0.calledOnce);
assert.ok(spy0.calledWith('id', "1"));

Stubs

Test stubs are functions (spies) with pre-programmed behaviors. They support spies interface along with other methods to alter stub’s behavior.

Use a stub when you want to:
  • Control a method’s behavior from a test to force the code down a specific path. Examples include forcing a method to throw an error in order to test error handling.

  • When you want to prevent a specific method from being called directly (possibly because it triggers undesired behavior, such as a XMLHttpRequest or similar).

Example,

  • Intercepting Method Calls
"test should stub method differently based on arguments": function () {
    var callback = sinon.stub();
    callback.withArgs(42).returns(1);
    callback.withArgs(1).throws("TypeError");

    callback(); // No return value, no exception
    callback(42); // Returns 1
    callback(1); // Throws TypeError
}
  • Testing Methods Making Network Calls
sinon.stub(jQuery, "ajax");
getTodos(42, sinon.spy());
assert(jQuery.ajax.calledWithMatch({ url: "/todo/42/items" }));

Mocks

Mocks (and mock expectations) are fake methods (like spies) with pre-programmed behavior (like stubs) as well as pre-programmed expectations. A mock will fail your test if it is not used as expected.

When to use mocks?

Mocks should only be used for the method under test. In every unit test, there should be one unit under test. If you want to control how your unit is being used and like stating expectations upfront (as opposed to asserting after the fact), use a mock.

Rule of thumb for using mocks are you should not be using mock if you are not using assertions.

"test should call all subscribers when exceptions": function () {
    var myAPI = { method: function () {} };

    var spy = sinon.spy();
    var mock = sinon.mock(myAPI);
    mock.expects("method").once().throws();

    PubSub.subscribe("message", myAPI.method);
    PubSub.subscribe("message", spy);
    PubSub.publishSync("message", undefined);

    mock.verify();
    assert(spy.calledOnce);
}

Assertions

sinon.assert.calledOnce(spy);
sinon.assert.calledWith(spy, message);
sinon.assert.callCount(spy, number);