EmberJS测试首次在phantomjs中运行失败

时间:2016-12-10 14:26:18

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

问题

我有/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在服务器模式下运行测试,则相同的测试失败;但是,当我按输入重新运行测试时,所有测试都会通过。

失败消息始终相同,如下所示:

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: |

在所有测试运行后,测试会发出异常:

# 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的经验,但我至少会期望它给我一个错误 - 它实际上什么也没做。

在我的Ember应用程序中感觉好像phantomjs之间存在计时问题,可能是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

根据丹尼尔的建议,我加入了延迟测试:

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

2 个答案:

答案 0 :(得分:1)

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

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

  2. 使用setTimeout而不是andThen,看看它是否是时间问题。

  3. 用任何内容替换身份验证操作代码,以确保它不会导致测试失败。

  4. 您还可以重写测试,在JavaScript中的某些事件之后放置您的assert.ok。例如,您可以模拟验证操作或观察者错误属性。您可以通过在验收环境中使用查找或注册来执行此操作 - 来自我的一个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, () => { ... assert here }而不是Ember.run.later。或者您可以尝试仅使用Ember.run代替Ember.run.later

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

答案 1 :(得分:1)

我认为您看到的错误(Invalid email/password)是(模拟)服务器响应,并指出您在测试中使用的模拟或凭据出现问题。

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