Old Guides - You are viewing the guides for Ember v3.12.0. VIEW v3.28.0
Edit Page

Testing Basics


Unit tests (as well as container tests) are generally used to test a small piece of code and ensure that it is doing what was intended. Unlike application tests, they are narrow in scope and do not require the Ember application to be running.

Let's have a look at a common use case - testing a service - to understand the basic principles of testing in Ember. This will set the foundation for other parts of your Ember application such as controllers, components, helpers and others. Testing a service is as simple as creating a container test, looking up the service on the application's container and running assertions against it.

Testing Computed Properties

Let's start by creating a service that has a computedFoo computed property based on a foo property.

import Service from '@ember/service';
import { computed } from '@ember/object';

export default Service.extend({
  foo: 'bar',

  computedFoo: computed('foo', function() {
    return `computed ${this.foo}`;
  })
});

Within the test for this object, we'll lookup the service instance, update the foo property (which should trigger the computed property), and assert that the logic in our computed property is working correctly.

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Service | some thing', function(hooks) {
  setupTest(hooks);

  test('should correctly concat foo', function(assert) {
    const someThing = this.owner.lookup('service:some-thing');
    someThing.set('foo', 'baz');

    assert.equal(someThing.get('computedFoo'), 'computed baz');
  });
});

See that first, we are creating a new testing module using the QUnit.module function. This will scope all of our tests together into one group that can be configured and run independently from other modules defined in our test suite. Also, we have used setupTest, one of the several test helpers provided by ember-qunit. The setupTest helper provides us with some conveniences, such as the this.owner object, that helps us to create or lookup objects which are needed to setup our test. In this example, we use the this.owner object to lookup the service instance that becomes our test subject: someThing. Note that in a unit test you can customize any object under test by setting its properties accordingly. We can use the set method of the test object to achieve this.

Testing Object Methods

Next let's look at testing logic found within an object's method. In this case the testMethod method alters some internal state of the object (by updating the foo property).

import Service from '@ember/service';

export default Service.extend({
  foo: 'bar',

  testMethod() {
    this.set('foo', 'baz');
  }
});

To test it, we create an instance of our class SomeThing as defined above, call the testMethod method and assert that the internal state is correct as a result of the method call.

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Service | some thing', function(hooks) {
  setupTest(hooks);

  test('should update foo on testMethod', function(assert) {
    const someThing = this.owner.lookup('service:some-thing');

    someThing.testMethod();

    assert.equal(someThing.get('foo'), 'baz');
  });
});

In case the object's method returns a value, you can simply assert that the return value is calculated correctly. Suppose our object has a calc method that returns a value based on some internal state.

import Service from '@ember/service';

export default Service.extend({
  count: 0,

  calc() {
    this.incrementProperty('count');
    return `count: ${this.count}`;
  }
});

The test would call the calc method and assert it gets back the correct value.

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Service | some thing', function(hooks) {
  setupTest(hooks);

  test('should return incremented count on calc', function(assert) {
    const someThing = this.owner.lookup('service:some-thing');

    assert.equal(someThing.calc(), 'count: 1');
    assert.equal(someThing.calc(), 'count: 2');
  });
});

Testing Observers

Suppose we have an object that has a property and a method observing that property.

import Service from '@ember/service';
import { observer } from '@ember/object';

export default Service.extend({
  foo: 'bar',
  other: 'no',

  doSomething: observer('foo', function() {
    this.set('other', 'yes');
  })
});

In order to test the doSomething method we create an instance of SomeThing, update the observed property (foo), and assert that the expected effects are present.

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Service | some thing', function(hooks) {
  setupTest(hooks);

  test('should set other prop to yes when foo changes', function(assert) {
    const someThing = this.owner.lookup('service:some-thing');

    someThing.set('foo', 'baz');
    assert.equal(someThing.get('other'), 'yes');
  });
});

Skipping tests

Some times you might be working on a feature, but know that a certain test will fail so you might want to skip it. You can do it by using skip:

import { test, skip } from 'qunit';

test('run this test', function(assert) {
    assert.ok(true)
});

skip('skip this test', function(assert) {
    assert.ok(true)
});