19、Ember.js 应用测试与第三方认证集成

Ember.js 应用测试与第三方认证集成

1. Ember.js 应用单元测试执行

要执行 Ember.js 应用的单元测试,可运行以下命令:

phantomjs run-qunit.js http://localhost:8081/index-test.html

注意, index-test.html 文件可在 GitHub 仓库中找到: https://github.com/joachimhs/Montric/blob/Ember.js-in-Action-Branch/Montric.View/src/main/webapp/index-test.html

PhantomJS 的第一个参数是要执行的脚本,其他参数作为测试脚本的输入参数。这里, run-qunit.js 脚本接收用于执行 QUnit 脚本的 URL,它会加载指定的 URL,并设置 QUnit 所需的监听器和钩子,以报告单元测试的进度和测试失败情况。

2. 集成测试概述

单元测试关注的是测试单个工作单元,如单个函数和单个类;而集成测试则关注集成应用的不同层,并对应用进行更广泛的测试。根据需求和测试设置,可以隔离应用的特定部分来测试其独立需求,也可以测试应用的某些功能的完整流程。

在 JavaScript 应用的不同层进行集成测试有很多可用的工具,一些比较流行的选择包括 Mocha、Capybara、Selenium WebDriver 和 Casper.js。由于已经在单元测试中使用了 QUnit,所以继续使用 QUnit 和 PhantomJS,并引入第三个库 Sinon.js(可从 http://sinonjs.org 下载)。

Sinon 库提供以下功能:
- Stubs :提供有效但静态结果的对象。无论向存根传入什么,都会得到相同的响应。存根还会提供有关哪些方法被执行以及执行次数的信息。
- Mocks :工作方式类似于存根,但还包含断言,允许测试输入和输出。
- Spies :允许报告与模拟对象相同的信息。与模拟对象提供预编程功能不同,间谍不实现伪造方法,允许在不改变功能的情况下监视方法。
- Fake objects :行为类似于真实对象,但方式更简单。常见的例子是将数据存储在内存中而不是真实数据库中的数据访问对象。注意,Sinon 本身不提供此功能。

3. 引入 Sinon 进行集成测试

这里将集成测试重点放在客户端应用上,在进行此类集成测试时,不关注与服务器端的通信。因此引入 Sinon 这个模拟框架,它可以模拟服务器端通信,让每个测试指定一个虚假的服务器端响应。

首先下载 sinon.js sinon-server.js sinon-qunit.js ,并将它们包含在 index-integration-test.html 文件中。

以 Montric 的警报管理功能为例编写集成测试。Montric 允许用户为收集的指标设置自定义警报阈值,GUI 为用户提供了添加、修改和删除系统中警报的功能。

4. 新增警报的集成测试

要验证通过输入新警报名称并点击“添加”按钮将新警报添加到系统的功能,需要按以下步骤操作:

4.1 启用 Sinon 虚假服务器

index-integration-test.html 文件中,在加载 Montric 应用之前,包含以下脚本:

<script type="text/javascript">
    Montric.server = sinon.fakeServer.create();  
</script>

此代码创建一个新的 sinon.fakeServer 实例,并将其存储在 Montric.server 属性中,以便测试可以检索该实例。

4.2 测试模块设置
var alertAdminController;
module("Montric.AdministrationAlertsController", {
    setup: function() {
        console.log('Admin Alerts Controller Module setup');
        Montric.server.autoRespond = true;           
        Montric.server.respondWith("GET", "/alert_models",              
               [200, { "Content-Type": "text/json" },
                  '{"alert_models":[]}'
                 ]);

        Montric.server.respondWith("POST", "/alert_models",             
                [200, { "Content-Type": "text/json" },
                '{"alert_model":{"alert_source":"null",
                "id":"New Alert","alert_delay":0,
                "alert_plugin_ids":[],"alert_notifications":"",
                "alert_activated":false,
                "alert_type":"greater_than"}}'
                ]);

        Ember.run(function() {
            alertAdminController = 
Montric.__container__.lookup("controller:administrationAlerts");  

        }); 
    },
    teardown: function() {
    }
});

首先配置虚假服务器自动响应 Ajax 请求,然后创建 Montric 将发出的两个 Ajax 请求的响应。第一个响应在应用加载时发出,返回一个空数组;第二个响应模拟创建新警报并发送到服务器时服务器的回复。最后,获取应用实例化的 Montric.AdministrationAlertsController ,并将其存储在 alertAdminController 变量中。

4.3 测试新增警报
var testCallbacks = {                      
    verifyContentLength: function() {                                
        Montric.reset();
        if (alertAdminController.get('content.length') > 0 ) {
            strictEqual(1, alertAdminController.get('content.length'), 
                    "Expecting one alert. Got: " + 
                    alertAdminController.get('content.length'));        
            QUnit.start();                        
        }
    }
};
asyncTest("Create a new Alert and verify that it is shown", 2, 
        function() {                                                    
    ok(alertAdminController, "Exepcting a non-null 
AdministrationAlertsController");             

    alertAdminController.get('content').addObserver('length', testCallbacks, 
'verifyContentLength');                  

    alertAdminController.set('newAlertName', 'New Alert');           
    alertAdminController.createNewAlert();        
});

由于测试的是异步代码,所以使用 QUnit 的 asyncTest 函数。该函数会执行测试函数内的所有内容,但直到调用 start() 函数才会退出测试。

测试开始时,重置应用,验证是否从 Ember.js 容器中获取到有效的控制器。然后更新警报名称输入框的文本为“New Alert”,并触发控制器的 createNewAlert() 函数,模拟用户点击“添加”按钮。为了验证创建的警报确实存在于警报列表中,需要在 alertAdminController content.length 属性上添加一个观察者,当该属性发生变化时,调用 verifyContentLength 回调函数。在回调函数中,断言内容数组中恰好有一个项目,并且其 ID 是“New Alert”,然后调用 QUnit.start() 函数进行下一个测试。

5. 使用 Ember.Instrumentation 进行性能测试

可以通过多种方式对 Ember.js 应用进行性能测试,一种方法是使用 Ember.js 内置的检测 API。要使用此 API,需要注册要获取测量数据的事件,并实现 before after 函数。

以下代码展示了如何通过订阅 render.view 事件来获取性能指标:

Ember.subscribe('render.view', {               
    ts: null,                                
    before: function(name, timestamp, payload) {
        ts = timestamp;
    },
    after: function(name, timestamp, payload, beforeRet) { 
        console.log('instrument: ' + name + 
                " " + JSON.stringify(payload) + 
                " took:" + (timestamp - ts));
    } 
});

代码很简单,首先订阅 render.view 事件,在视图渲染之前和之后都会触发事件。在 before() 函数中记录时间戳,在 after() 函数中打印出视图渲染所需的时间。

可以订阅的事件包括:
- render.view
- render.render.boundHandlebars
- render.render.metamorph
- render.render.container
- * - (all)

尽管 Ember.Instrumentation 提供的功能目前有限,但它仍然是一种有用且快速的方法来收集应用渲染过程的统计信息。

6. 第三方认证集成 - Mozilla Persona

现在熟悉了 Ember.js 架构,知道如何使用它来构建丰富的 Web 应用。但如何为应用用户提供无缝的登录体验可能还不太清楚。这里介绍通过集成 Mozilla Persona 实现单点登录解决方案。

Mozilla Persona 是一个完整的、开源的、独立的用户认证和授权平台,它允许应用通过用户的个人电子邮件账户对数百万用户进行认证。

使用第三方认证提供商有很多优点,最重要的是应用无需承担保管用户名和密码的责任,同时提供安全的异地存储和密码哈希,保护用户信息免受黑客攻击。

Mozilla Persona 负责以下用户认证和授权方面:
- 电子邮件注册
- 电子邮件验证

可以使用 Ember Router 的结构将用户认证集成到应用中。Persona 的核心思想是用户的在线身份应完全属于他们自己,它允许用户将一个或多个电子邮件账户关联到其身份,从而使用单个用户名(电子邮件)和密码登录一系列网站和应用。此外,由于 Persona 是由一个以开源为核心的非营利组织创建的,依赖它可以赢得用户的信任。

下面是使用 Persona 进行认证的流程:

graph LR
    A[用户点击登录] --> B[调用 Persona 登录接口]
    B --> C[Persona 验证用户邮箱和密码]
    C -->|验证成功| D[返回认证信息给应用]
    C -->|验证失败| E[提示用户重新输入]
    D --> F[应用使用认证信息进行后续操作]

总结

测试 JavaScript 应用有多种方法,虽然很多工具的基本思想和目标相同,但尽量减少引入的库和框架的数量。尽管这里的大多数示例基于 QUnit,但还有许多其他测试库可满足不同的需求和测试风格。

JavaScript 作为一个平台在过去几年中发展迅速,但可用的工具往往存在不足、不完整或集成不良的问题,这使得在不同层面对应用进行测试的策略实施比在其他更成熟的语言中更困难。不过,JavaScript 工具业务正在蓬勃发展,新的强大工具不断涌现。

PhantomJS 正迅速成为无头测试的事实上的标准,大多数其他库和工具都支持它。随着时间的推移,哪些工具会更受欢迎将变得更加清晰,从而使集成变得更加容易。

通过上述内容,应该能够设计、编写和测试基于 Ember.js 的应用,并且对 Ember.js 的核心概念和功能有了深入的理解。同时,了解了如何使用第三方认证系统(如 Mozilla Persona)为应用提供无缝的登录体验。

Ember.js 应用测试与第三方认证集成

7. 集成 Mozilla Persona 的具体步骤
7.1 引入 Persona 脚本

在 HTML 文件中引入 Persona 的 JavaScript 库,示例代码如下:

<script src="https://login.persona.org/include.js"></script>
7.2 实现登录按钮功能

在页面中添加一个登录按钮,并为其绑定点击事件,触发 Persona 的登录流程。示例代码如下:

<button id="loginButton">登录</button>
<script>
    document.getElementById('loginButton').addEventListener('click', function() {
        navigator.id.request();
    });
</script>
7.3 处理登录回调

当用户登录成功后,Persona 会调用 onlogin 回调函数,在该函数中可以获取用户的认证信息,并进行后续处理。示例代码如下:

navigator.id.watch({
    loggedInUser: null,
    onlogin: function(assertion) {
        // 将 assertion 发送到服务器进行验证
        var xhr = new XMLHttpRequest();
        xhr.open('POST', '/verify', true);
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200) {
                var response = JSON.parse(xhr.responseText);
                if (response.status === 'okay') {
                    // 登录成功,进行后续操作
                    console.log('登录成功,用户邮箱:', response.email);
                } else {
                    // 登录失败,提示用户
                    console.log('登录失败:', response.reason);
                }
            }
        };
        xhr.send(JSON.stringify({ assertion: assertion }));
    },
    onlogout: function() {
        // 处理用户注销操作
        var xhr = new XMLHttpRequest();
        xhr.open('POST', '/logout', true);
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.send();
    }
});
7.4 服务器端验证

在服务器端接收到客户端发送的 assertion 后,需要将其发送到 Persona 的验证服务进行验证。以下是一个使用 Node.js 和 Express 框架的示例代码:

const express = require('express');
const app = express();
const request = require('request');

app.post('/verify', function(req, res) {
    var assertion = req.body.assertion;
    var audience = 'http://yourdomain.com'; // 替换为你的域名

    request.post({
        url: 'https://verifier.login.persona.org/verify',
        form: {
            assertion: assertion,
            audience: audience
        }
    }, function(error, response, body) {
        if (!error && response.statusCode === 200) {
            var verification = JSON.parse(body);
            if (verification.status === 'okay') {
                res.json({ status: 'okay', email: verification.email });
            } else {
                res.json({ status: 'failure', reason: verification.reason });
            }
        } else {
            res.json({ status: 'failure', reason: '验证服务出错' });
        }
    });
});

app.post('/logout', function(req, res) {
    // 处理用户注销逻辑
    res.json({ status: 'okay' });
});

const port = 3000;
app.listen(port, function() {
    console.log(`服务器运行在端口 ${port}`);
});
8. 基于 Cookie 的重新认证

为了提供更流畅的用户体验,可以使用 HTTP Cookie 实现重新认证。当用户登录成功后,服务器可以设置一个 Cookie,下次用户访问应用时,服务器可以根据 Cookie 中的信息自动验证用户身份。

以下是一个简单的流程说明:
1. 用户登录成功后,服务器设置一个包含用户认证信息的 Cookie。
2. 下次用户访问应用时,客户端发送请求到服务器,请求中包含 Cookie。
3. 服务器检查 Cookie 中的信息,如果有效,则自动验证用户身份,无需用户再次登录。

graph LR
    A[用户首次登录] --> B[服务器设置 Cookie]
    B --> C[用户下次访问应用]
    C --> D[客户端发送请求并携带 Cookie]
    D --> E[服务器检查 Cookie]
    E -->|有效| F[自动验证用户身份]
    E -->|无效| G[提示用户重新登录]
9. 总结与展望

通过上述内容,我们详细介绍了 Ember.js 应用的测试方法,包括单元测试、集成测试和性能测试,以及如何集成第三方认证系统 Mozilla Persona 为应用提供无缝的登录体验。

在测试方面,QUnit、PhantomJS 和 Sinon 等工具为我们提供了强大的测试能力,可以确保应用的质量和稳定性。而 Ember.Instrumentation 则为我们提供了一种简单有效的性能测试方法,帮助我们优化应用的性能。

在认证方面,Mozilla Persona 作为一个开源的第三方认证平台,为我们提供了一种安全、便捷的用户认证解决方案。通过集成 Persona,我们可以避免处理复杂的用户认证逻辑,同时保护用户的隐私和安全。

未来,随着 Web 技术的不断发展,Ember.js 和相关工具也会不断更新和完善。我们可以期待更多的测试工具和认证系统的出现,为我们的开发工作带来更多的便利和可能性。同时,我们也需要不断学习和掌握新的技术,以适应不断变化的市场需求。

总之,掌握 Ember.js 应用的测试和认证技术,对于开发高质量、高性能的 Web 应用至关重要。希望本文能够对大家有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值