【问题标题】:EmberJS test fails first time running in phantomjsEmberJS 测试第一次在 phantomjs 中运行失败
【发布时间】:2016-12-10 14:26:18
【问题描述】:

问题

我有一个/login 路由,它使用ember-simple-auth 来实现身份验证。在测试期间,ember-cli-mirage 用于模拟后端。用户通过提供他们的电子邮件地址和密码登录。

对于这条路线,我总共有 4 次验收测试,类似于下面的测试:

test('should show error message for invalid email', function(assert) {
  visit('/login');
  fillIn('input#email', 'invalid-email');
  fillIn('input#password', 'invalid-password');
  click('button.button');
  andThen(function() {
    assert.equal(find('div.notification').text(), "Invalid email/password");
  });
});

当我使用ember t 运行测试时,只有文件中的第一个测试失败。如果我将此测试注释掉,则下一个测试会失败,依此类推。如果我使用ember t -s 在服务器模式下运行测试,同样的测试会失败;但是,当我按 enter 重新运行测试时,所有测试都通过了。

失败信息总是一样的,如下图:

not ok 7 PhantomJS 2.1 - Acceptance | login: should show error message for invalid email
    ---
        actual: >

        expected: >
            Invalid email/password
        stack: >
            http://localhost:7357/assets/tests.js:22:19
            andThen@http://localhost:7357/assets/vendor.js:48231:41
            http://localhost:7357/assets/vendor.js:48174:24
            isolate@http://localhost:7357/assets/vendor.js:49302:30
            http://localhost:7357/assets/vendor.js:49258:23
            tryCatch@http://localhost:7357/assets/vendor.js:68726:20
            invokeCallback@http://localhost:7357/assets/vendor.js:68738:21
            publish@http://localhost:7357/assets/vendor.js:68709:21
            http://localhost:7357/assets/vendor.js:48192:24
            invoke@http://localhost:7357/assets/vendor.js:10892:18
            flush@http://localhost:7357/assets/vendor.js:10960:15
            flush@http://localhost:7357/assets/vendor.js:11084:20
            end@http://localhost:7357/assets/vendor.js:11154:28
            run@http://localhost:7357/assets/vendor.js:11277:19
            run@http://localhost:7357/assets/vendor.js:32073:32
            http://localhost:7357/assets/vendor.js:48783:24
        Log: |

在所有测试都运行之后,test 会发出一个异常:

# tests 60
# pass  59
# skip  0
# fail  1
Not all tests passed.
Error: Not all tests passed.
    at EventEmitter.getExitCode (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/lib/app.js:434:15)
    at EventEmitter.exit (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/lib/app.js:189:23)
    at /home/jon/projects/jonblack/wishlist-web/node_modules/testem/lib/app.js:103:14
    at tryCatcher (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/promise.js:510:31)
    at Promise._settlePromise (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/promise.js:567:18)
    at Promise._settlePromise0 (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/promise.js:612:10)
    at Promise._settlePromises (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/promise.js:691:18)
    at Async._drainQueue (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/async.js:138:16)
    at Async._drainQueues (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/async.js:148:10)
    at Immediate.Async.drainQueues (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/async.js:17:14)
    at runCallback (timers.js:637:20)
    at tryOnImmediate (timers.js:610:5)
    at processImmediate [as _immediateCallback] (timers.js:582:5)

这似乎很奇怪,因为测试失败而不仅仅是报告测试失败,所以它可能是相关的。

在 Firefox 和 Chromium 中运行测试可以正常工作,就像在开发模式下运行应用程序并手动登录一样。问题仅限于phantomjs。

我对另一条路线进行了其他验收测试,这些都通过了。似乎仅限于/login路由,说明可能与身份验证有关。

调试

我尝试通过将pauseTest() 添加到测试并将"phantomjs_debug_port": 9000 添加到testem.js 进行调试,但是当我使用调试控制台时,Firefox 和 Chromium 都没有执行任何操作。这可能是我缺乏调试 phantomjs 的经验,但我至少希望它会给我一个错误 - 它实际上什么也没做。

感觉好像 phantomjs 和我的 Ember 应用程序中可能的 ember-simple-auth 之间存在时间问题。

我对调试 phantomjs 问题或 Ember 验收测试失败的经验并不多,因此感谢您提供任何帮助。

版本

ember-cli 2.10.0
ember-simple-auth 1.1.0
ember-cli-mirage 0.2.4

更新 1

按钮位于login-form 组件内:

<form {{action 'login' on='submit'}}>
  <p class="control has-icon">
    {{input value=email id='email' placeholder='email' class='input'}}
    <i class="fa fa-envelope"></i>
  </p>
  <p class="control has-icon">
    {{input value=password id='password' placeholder='password'
            type='password' class='input'}}
    <i class="fa fa-lock"></i>
  </p>
  <p class="control">
  <button class="button is-success" disabled={{isDisabled}}>Log In</button>
  </p>
</form>

组件的登录操作只是调用传入的登录处理程序:

import Ember from 'ember';

export default Ember.Component.extend({
  email: "",
  password: "",

  isDisabled: Ember.computed('email', 'password', function() {
    return this.get('email') === "" || this.get('password') === "";
  }),

  actions: {
    login() {
      var email = this.get('email');
      var password = this.get('password');
      this.attrs.login(email, password);
    }
  }
});

哪个是登录控制器中的authenticate方法:

import Ember from 'ember';

export default Ember.Controller.extend({
  session: Ember.inject.service(),

  actions: {
    authenticate(email, password) {
      this.get('session').authenticate('authenticator:oauth2', email, password).catch((data) => {
        this.set('errors', data['errors']);
      });
    }
  }
});

更新 2

按照 Daniel 的建议,我在测试中添加了延迟:

test('should show error message for invalid email', function(assert) {
  visit('/login');
  fillIn('input#email', 'invalid-email');
  fillIn('input#password', 'invalid-password');
  click('button.button');
  andThen(function() {
    Ember.run.later(this, function() {
      assert.equal(find('div.notification').text(), "Invalid email/password");
    }, 0);
  });
});

仅使用 Ember.run.later 测试仍然失败,但将其放入 andThen 会使其通过。你注意到奇怪的部分了吗? 延迟为 0 毫秒。

我仍然想为此找到一个解释,因为我不相信这会在运行测试的任何机器上运行相同。

更新 3

今天我有一个惊喜:突然测试又开始了!

我添加了一条带有验收测试的新路线。路由本身是经过身份验证的路由,因此测试使用来自 ember-simple-auth 的 authenticateSession 测试助手进行身份验证。

当我删除使用此帮助程序的测试时,错误返回!

我不确定这意味着什么。感觉问题出在 ember-simple-auth 上,但助手解决另一个时间问题也可能是一个巨大的巧合。

我们去兔子洞……

更新 4

下面是 ember-cli-mirage 中 auth 端点的配置:

this.post('/token', function({db}, request) {
  var data = parsePostData(request.requestBody);

  if (data.grant_type === 'password') {
    // Lookup user in the mirage db
    var users = db.users.where({ email: data.username });
    if (users.length !== 1) {
      return new Mirage.Response(400, {'Content-Type': 'application/json'}, {
        errors: [{
          id: 'invalid_login',
          status: '400',
          title: 'Invalid email/password',
        }]
      });
    }
    var user = users[0];

    // Check password
    if (data.password === user.password) {
      if (!user.active) {
        return new Mirage.Response(400, {'Content-Type': 'application/json'}, {
          errors: [{
            id: 'inactive_user',
            status: '400',
            title: 'Inactive user',
          }]
        });
      } else {
        return new Mirage.Response(200, {
          'Content-Type': 'application/json'
        }, {
          access_token: 'secret token!',
          user_id: user.id
        });
      }
    } else {
      return new Mirage.Response(400, {'Content-Type': 'application/json'}, {
        errors: [{
          id: 'invalid_login',
          status: '400',
          title: 'Invalid email/password',
        }]
      });
    }
  } else {
    return new Mirage.Response(400, {'Content-Type': 'application/json'}, {
      errors: [{
        id: 'invalid_grant_type',
        status: '400',
        title: 'Invalid grant type',
      }]
    });
  }
});

this.post('/revoke', function(db, request) {
  var data = parsePostData(request.requestBody);

  if (data.token_type_hint === 'access_token' ||
      data.token_type_hint === 'refresh_token') {
    return new Mirage.Response(200, {'Content-Type': 'application/json'});
  } else {
    return new Mirage.Response(400, {'Content-Type': 'application/json'},
                               {error: 'unsupported_token_type'});
  }
});

更新 5

这是我的config/environment.js 文件:

/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'wishlist-web',
    environment: environment,
    rootURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
      },
      EXTEND_PROTOTYPES: {
        // Prevent Ember Data from overriding Date.parse.
        Date: false
      }
    },

    APP: {
    }
  };

  if (environment === 'development') {
  }

  if (environment === 'test') {
    // Testem prefers this...
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {
    ENV.ServerTokenEndpoint = 'http://localhost:9292/token';
    ENV.ServerTokenRevocationEndpoint = 'http://localhost:9292/revoke';
    ENV.ApiHost = 'http://localhost:9292';
  }

  return ENV;
};

【问题讨论】:

  • 当你点击button.button时,你如何创建一个promise?你能提供创建承诺的代码吗? (我的猜测是在 phantomjs 环境中无法解决 promise。)

标签: ember.js phantomjs ember-simple-auth


【解决方案1】:

您可以在此处尝试调试此问题。

  1. 您可以从按钮中删除{{isDisabled}},以确保在您尝试单击它时它没有被禁用。

  2. 使用 setTimeout 代替 andThen 看看是不是时间问题。

  3. 将身份验证操作代码替换为空,以确保它不会导致您的测试失败。

您还可以重写 测试以将您的 assert.ok 放在 JavaScript 中的某个事件之后。例如,您可以模拟身份验证操作或观察者错误属性。您可以通过在验收环境中使用查找或注册来做到这一点 - 我的 Ember CLI 插件之一的测试可以帮助您 - ember-link-action/tests/acceptance/link-action-test.js

编辑

看到对你有用的经验告诉我你应该尝试两件事。

对于此代码:

andThen(function() {
    Ember.run.later(this, function() {
      assert.equal(find('div.notification').text(), "Invalid email/password");
    }, 0);
  });

您可以尝试使用Ember.run.scheduleOnce('afterRender', this, () =&gt; { ... assert here } 而不是使用Ember.run.later。或者您可以尝试只使用Ember.run 而不是Ember.run.later

结论:解决此问题的关键可能是将您的断言放入 Ember Run Loop 中。

【讨论】:

  • 很好的建议。我对{{isDisabled}} 进行了组件测试,效果很好;无论如何删除它并没有帮助。使用第二个选项,我能够通过测试(通过使用Ember.run.later 延迟断言,但我仍然需要使用andThen。我已经更新了我的问题,因为这感觉它很容易中断(例如,如果CI服务器负载不足,延迟不够高)。我仍然想找到根本原因。
  • 刚刚注意到一些奇怪的事情:延迟设置为 0 毫秒它可以工作。
  • 嘿乔恩,我已经更新了答案,因为你发现了一些非常有趣的东西。 ;) 我很好奇它是否有帮助......
  • 很遗憾,Ember.run.scheduleOnceEmber.run 都没有帮助。可能是 Ember.run.later 的调用即使有 0 毫秒的延迟仍然需要足够长的时间,但我在这里抓住了稻草。
  • 您是否尝试重写测试以查找控制器:登录、addObserver 以查找“错误”,并在该观察者 INSIDE Ember.run.scheduleOnce('afterRender', ...) 中运行断言?
【解决方案2】:

我假设您看到的错误 (Invalid email/password) 是(模拟)服务器响应,表明模拟或您在测试中使用的凭据有问题。

我也不会使用 mirage 来模拟身份验证请求。 mirage(就像 Jason API 一样)是基于资源的,而不是非常适合身份验证的东西。

【讨论】:

  • 是的,“无效的电子邮件/密码”消息来自模拟 (ember-cli-mirage)。我已经更新了问题以包含此配置。我同意 ember-cli-mirage 不太适合模拟非基于资源的端点,但我不知道其他任何事情,也没有那么大的努力。你认为这可能是原因吗?这可以解释为什么当另一个带有 ember-simple-auths authenticateSession 助手的测试首先运行时测试通过了吗?
  • 你能分享你的config/environment.js吗?
  • 添加了我的config/environment.js。据我所知,只有生产设置是新的,但在测试期间不应使用这些设置。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-19
  • 1970-01-01
  • 2012-08-14
  • 1970-01-01
  • 2021-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多