ember.jsember-dataember-testing

EmberJS 2.13 unit testing: wrapping synchronous code in a run loop?


This is repeated a few times in the current Ember documentation, so I feel like I must be missing something. Let's take the simplest example I found.

Why is the call to levelUp considered asynchronous to warrant wrapping it in the run loop?
incrementProperty is synchronous, and as far as I can tell, so is set (but I could be mistaken here)

player.js

import DS from 'ember-data';

export default DS.Model.extend({
  level:     DS.attr('number', { defaultValue: 0 }),
  levelName: DS.attr('string', { defaultValue: 'Noob' }),

  levelUp() {
    let newLevel = this.incrementProperty('level');
    if (newLevel === 5) {
      this.set('levelName', 'Professional');
    }
  }
});

player-test.js

import { moduleForModel, test } from 'ember-qunit';
import Ember from 'ember';

moduleForModel('player', 'Unit | Model | player', {
  // Specify the other units that are required for this test.
  needs: []
});

test('should increment level when told to', function(assert) {
  // this.subject aliases the createRecord method on the model
  const player = this.subject({ level: 4 });

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

  assert.equal(player.get('level'), 5, 'level gets incremented');
  assert.equal(player.get('levelName'), 'Professional', 'new level is called professional');
});

Solution

  • First of all, you are absolutely right. It is not well-described anywhere in the guides.

    In testing mode, autorun is disabled. You can read further from the guides about this.

    But changing the value in model triggers a run-loop. You can see that at this twiddle. The result is:

    Assertion Failed: You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in a run

    (By the way, both set and incrementProperty trigger this run-loop as your guess.)

    Then here is the run loop source:

    1. DS.attr returns a computed property with set.
    2. The set function triggers an event.
    3. At the end, a run loop is triggered.