问题
我有一个/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: |
进行了所有测试,测试排放出例外:
# 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和某些东西之间的时机似乎存在时机问题。
我并不是说调试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 I建议的测试延迟:
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
今天我感到惊讶:突然,测试再次工作!
我添加了一个带有接受测试的新路线。该路线本身是一条经过身份验证的路线,因此测试使用authenticateSession
测试助手从Ember-Simple-auth进行身份验证。
当我删除使用此助手的测试时,错误返回!。
我不确定这意味着什么。感觉就像是ember-simple-auth,但助手解决另一个时机问题也可能是一个巨大的巧合。
我们去的兔子洞...
更新4
以下是Ember-Cli-Mirage中验证端点的配置:
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;
};
您在这里尝试调试此问题。
-
您可以从按钮中删除
{{isDisabled}}
,以确保在尝试单击它时不会禁用它。 -
使用settimeout而不是andthen,看看是否是正时问题。
-
无需替换身份验证的操作代码,以确保它不会导致测试失败。
您也可以重写测试以在JavaScript中进行某些事件后将您的assert.ok放置。例如,您可以模拟身份验证的操作或观察者错误属性。您可以通过在接受环境中使用查找或寄存器来执行此操作 - 我的Ember CLI附加子中的测试可以帮助您-Ember-link-action/tests/cactance/link-action-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中。
我会假设您看到的错误(Invalid email/password
)是(模拟)服务器响应,并指示您在测试中使用的模拟或凭据是错误的。
我也不会使用Mirage嘲笑身份验证请求。Mirage(就像Jason API一样)是基于资源的,而不是非常适合身份验证的东西。