EmberJS 2.13 单元测试:将同步代码包装在 运行 循环中?

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

这在当前的 Ember 文档中重复了几次,所以我觉得我一定遗漏了什么。让我们来看看 simplest example I found.

为什么对 levelUp 的调用被视为异步以保证将其包装在 运行 循环中?
incrementProperty is synchronous, and as far as I can tell, so is set(但我可能在这里弄错了)

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');
    }
  }
});

玩家-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');
});

首先,你是完全正确的。指南中的任何地方都没有很好地描述它。

在测试模式下,自动运行 被禁用。您可以从 the guides 中进一步阅读。

但是改变模型中的值会触发一个运行循环。你可以在this twiddle.结果是:

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

(顺便说一下,setincrementProperty 都会触发这个 运行-循环,正如你猜测的那样。)

那么这里是 运行 循环源:

  1. DS.attrreturns一个computed property with set
  2. set 函数 triggers an event
  3. 最后,a run loop is triggered

@ykaragol 在他的正确答案中对他的解释是绝对正确的,我没有什么要补充的,为什么你需要将你的代码包装在一个 运行 循环中;因为源代码在那里并且正在调用 emberRun.schedule,这需要一个 运行 循环。

我想解释的是关于您得到的断言错误的更多信息:"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"。这并不直接意味着异步操作(在进行 ajax 调用或触发计时器的意义上)就位。我们大多不知道,但是; Ember.js 确实使用 Ember.run 循环和各种 运行 队列,例如 syncactionsrenderafterRender 等。为了安排我们的代码的效果,以优化我们的应用程序的呈现。即使代码 this.set('levelName', 'Professional'); 看起来非常同步; Ember 将其包装在 运行 循环中,以便将计算的 属性 计算或其他更新缓冲在一起,以防止模板的多次渲染(因此降低性能)。

我只希望对 运行 循环、运行 队列或者如何以及为什么在测试中使用 运行 循环有更好的解释,但是没有:(