Google News
logo
Ember.js Interview Questions
Large organizations often use Ember.js to power sophisticated web applications. These apps may require collaboration among several teams, sometimes distributed around the world. Typically, responsibility is shared by dividing the application into one or more "sections". How this division is actually implemented varies from team to team.
 
Maintaining large monolithic applications poses the following challenges:
 
* Side effects : if you change something, it may be unclear how it could affect the rest of platform.

* Coordination :
 when you develop a new feature or make big changes, many teams may need to be in sync to approve it.

* Complexity :
 with a huge dependency tree and many layers of abstraction, developers cannot iterate quickly, and features suffer as a result.

* Killing Innovation :
 a/b testing a cutting-edge feature is hard to do without disrupting the rest of the app and the teams working on it.

* Slow Onboarding :
 new people coming into the team are overwhelmed.

Engines provide an antidote to these problems by allowing for distributed development, testing, and packaging of logically-grouped pieces of an application.
The flag status in the generated build is controlled by the @ember/canary-features package. This package exports a list of all available features and their current status.
 
A feature can have one of a three flags :
 
* true : The feature is present and enabled: the code behind the flag is always enabled in the generated build.

* null : The feature is present but disabled in the build output. It must be enabled at runtime.

* false : The feature is entirely disabled: the code behind the flag is not present in the generated build.

The process of removing the feature flags from the resulting build output is handled by defeatureify.
Ember provides 3 types of tests out of the box :
 
* Unit tests
* Rendering tests (previously known as integration tests)
* Application tests (previously known as acceptance tests)

Broadly speaking, these tests differ in two aspects :
* Which parts of your app they check for correctness. Having different types of tests help separate testing concerns.
* How fast they execute.
Container testing methods and computed properties follow previous patterns shown in Testing Basics because DS.Model extends Ember.Object.
 
Ember Data Models can be tested in a module that uses the setupTest helper.
 
Let's assume we have a Player model that has level and levelName attributes. We want to call levelUp() to increment the level and assign a new levelName when the player reaches level 5.
 
You can follow along by generating your own model with ember generate model player.
 
app/models/player.js :
import Model, { attr } from '@ember-data/model';

export default class Player extends Model {
  @attr('number', { defaultValue: 0 }) level;
  @attr('string', { defaultValue: 'Noob' }) levelName;

  levelUp() {
    let newLevel = this.level++;
    if (newLevel === 5) {
      this.levelName = 'Professional';
    }
  }
}
Now let's create a test which will call levelUp on the player when they are level 4 to assert that the levelName changes. We will use module together with the setupTest helper method :
 
tests/unit/models/player-test.js :
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { run } from '@ember/runloop';

module('Unit | Model | player', function(hooks) {
  setupTest(hooks);

  // Specify the other units that are required for this test.
  test('should increment level when told to', function(assert) {
    const player = run(() =>
      this.owner.lookup('service:store').createRecord('player')
    );

    // wrap asynchronous call in run loop
    run(() => player.levelUp());

    assert.equal(player.level, 5, 'level gets incremented');
    assert.equal(
      player.levelName,
      'Professional',
      'new level is called professional'
    );
  });
});​
Controllers can be tested using the setupTest helper which is part of the ember-qunit framework. The tests written for instances like Ember.Controller are also described as container tests.
 
Testing Controller Actions : Here we have a controller PostsController with two properties, a method that sets one of those properties, and an action named setProps.
 
You can follow along by generating your own controller with ember generate controller posts.
 
app/controllers/posts.js :
import Controller from '@ember/controller';
import { action } from '@ember/object';

export default class PostsController extends Controller {
  propA = 'You need to write tests';
  propB = 'And write one for me too';

  setPropB(str) {
    this.propB = str;
  }

  @action
  setProps(str) {
    this.propA = 'Testing is cool';
    this.setPropB(str);
  }
}
The setProps action directly sets one property, and calls the method to set the other. In our generated test file, Ember CLI already uses the module and the setupTest helpers to set up a test container:
 
tests/unit/controllers/posts-test.js :
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Controller | posts', function(hooks) {
  setupTest(hooks);
});
 
Next we use the owner API to gain access to the controller we'd like to test. Using the this.owner.lookup method we get the instance of the PostsController and can check the action in our test. The this.owner.lookup helper returns objects generated by the framework in your applications and is also exposed in tests for your usage. Here it will return a singleton instance of the PostsController.
 
tests/unit/controllers/posts-test.js :
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Controller | posts', function(hooks) {
  setupTest(hooks);

  test('should update A and B on setProps action', function(assert) {
    assert.expect(4);

    // get the controller instance
    let controller = this.owner.lookup('controller:posts');

    // check the properties before the action is triggered
    assert.equal(
      controller.propA,
      'You need to write tests',
      'propA initialized'
    );
    assert.equal(
      controller.propB,
      'And write one for me too',
      'propB initialized'
    );

    // trigger the action on the controller by using the `send` method,
    // passing in any params that our action may be expecting
    controller.send('setProps', 'Testing Rocks!');

    // finally we assert that our values have been updated
    // by triggering our action.
    assert.equal(controller.propA, 'Testing is cool', 'propA updated');
    assert.equal(controller.propB, 'Testing Rocks!', 'propB updated');
  });
});​