API 测试与生产部署全解析
1. API 测试基础
在进行 API 项目开发时,测试是确保代码质量的关键环节。回调函数的属性通常命名为
done
,用于表示特定测试用例的结束。以下是一个简化的异步测试用例示例:
describe('User', function() {
describe('#save()', function() {
it('should save without error', function(done) {
var user = new User('Luna');
user.save(done);
});
});
});
1.1 测试 BookSales 控制器的 create 方法
为了展示如何添加测试,我们以 BookSales 控制器的
create
方法为例。以下是完整的单元测试代码:
const assert = require("assert");
const restifyErrors = require("restify-errors")
const sinon = require("sinon")
const mongoose = require("mongoose")
const lib = require("../lib");
describe("Controllers", function () {
describe("BookSales", function() {
describe("#create", function() {
let BookSales;
//setup all we need for the tests
beforeEach(function() {
BookSales = require("../controllers/booksales")(lib);
sinon.spy(BookSales, "writeHAL")
})
//and tear down whatever we changed
afterEach(function(){
BookSales.writeHAL.restore();
})
//tests
it("should return an InvalidArgument exception if no body is provided in the request", function (done) {
BookSales.create({}, {}, function(err) {
assert.ok(err instanceof restifyErrors.InvalidArgumentError)
done();
})
})
it("should call the save method for the booksale model", function() {
//we'll spy on this method to understand when and how we call it
sinon.spy(mongoose.Model.prototype, "save")
BookSales.create({body: {totalAmount: 1}}, {})
assert.ok(mongoose.Model.prototype.save.calledOnce)
mongoose.Model.prototype.save.restore();
})
it("should call the writeHAL method", function() {
//we stub the method, so it can actually succeed even without a valid connection
sinon.stub(mongoose.Model.prototype, "save").callsFake( cb => cb() )
//we create a simple fake "json" property that will be called by writeHAL
BookSales.create({body: {totalAmount: 1}}, {json: () => {} })
assert.ok(BookSales.writeHAL.calledOnce)
mongoose.Model.prototype.save.restore();
})
})
})
})
1.2 测试要点分析
我们为测试创建了分组,采用
Controllers -> [Controller name] -> [Method name]
的结构。对于
create
方法,主要进行以下测试:
- 当请求中未提供请求体时,应抛出正确类型的错误消息。
- 调用正在创建的模型的
save
方法。
- 数据成功保存到数据库后,控制器应调用
writeHAL
方法以创建正确格式的响应。
这三个测试各有不同的机制:
- 第一个测试展示了如何使用
done
回调来处理异步函数。
- 第二个测试使用 SinonJS 创建方法的间谍(spy),以判断方法是否被调用。
- 第三个测试创建存根(stub),控制数据库插入操作,确保即使没有有效连接也能成功执行。
1.3 测试执行与结果
假设将上述代码添加到项目根目录下的
tests
文件夹中,使用以下命令执行测试:
$mocha tests/
测试结果分组显示,便于理解。即使测试全部通过,出现错误消息也是正常的,因为第一个测试就是为了测试错误类型。
2. 不同环境下的开发流程
在严肃的软件开发项目中,尤其是与 Web 相关的项目,通常会有不同的部署环境。常见的环境包括:
| 环境 | 描述 |
| — | — |
| 开发环境(Development) | 通常仅供开发人员使用,用于集成正在进行的工作,并查看与其他同事工作的交互情况。如果只有一名开发人员,也可以是其个人计算机。 |
| 质量保证环境(QA 或 QC) | 开发人员完成工作并认为准备好后,代码将部署到此环境。质量保证团队将从功能角度审查功能。 |
| 用户验收测试环境(UAT) | 作为预生产阶段,即将投入生产的代码将部署到此环境。客户或最终用户的子集将测试此版本的产品,以捕获可能遗漏的业务相关问题。 |
| 生产环境(Production) | 最终使用的代码将部署在此环境,是开发周期的最后阶段。 |
2.1 经典开发工作流
graph LR
A[开发环境] --> B[质量保证环境]
B --> C{是否通过 QA?}
C -- 是 --> D[用户验收测试环境]
C -- 否 --> A
D --> E{是否通过 UAT?}
E -- 是 --> F[生产环境]
E -- 否 --> A
2.2 环境差异与建议
一般来说,开发环境和生产环境在目标受众和可用性要求上截然不同,因此有所差异是正常的。在持续部署场景中,开发环境更活跃,简化其基础设施有助于降低维护成本和部署时间。
对于 QA 和 UAT 环境:
- QA 环境可以与开发环境相似,因为它是第二活跃的环境,并且有助于开发人员调试在自己环境中无法发现的错误。
- UAT 环境应与生产环境相似,以便在最终阶段之前检测到与基础设施相关的错误。
不过,这些建议应根据实际情况进行调整。
3. 生产环境的考虑因素
3.1 高可用性
在定义生产环境的架构时,高可用性是一个重要的考虑因素。高可用性意味着平台或系统在面对灾难或部分模块因技术问题出现故障时仍能保持功能正常。以下是两种实现高可用性的技术:
3.1.1 负载均衡器
当应用程序开始被大量使用时,传入的流量可能会导致服务器过载。此时可以选择垂直扩展或水平扩展:
-
垂直扩展
:为服务器添加更多资源(如内存或磁盘),但这种方法有其局限性。
-
水平扩展
:通过添加更多计算机(服务器)来增加计算能力。但当有多个面向客户端的 Web 服务器时,需要解决如何在它们之间分配负载的问题,这就需要使用负载均衡器。
常见的负载均衡器包括 Amazon 的 ELB、F5 Networks 和 Nginx(也可配置为负载均衡器)。负载均衡器根据预定义的规则分配传入流量,常见规则如下:
| 规则 | 描述 |
| — | — |
| 轮询(Round robin) | 每个新请求会依次访问不同的服务器,循环往复。 |
| 最少连接(Least connected) | 下一个传入请求将发送到活动连接最少的服务器。 |
| IP 哈希函数(IP hash function) | 根据客户端的 IP 地址进行哈希计算,为每个服务器分配一个哈希码。 |
使用负载均衡器时,如果服务器是有状态的,可能会遇到会话问题。对于 RESTful API,由于其无状态的特性,这个问题通常不会出现。但为了以防万一,可以使用粘性会话(sticky sessions)或将会话管理提取到单独的公共服务(如数据库)中。
3.1.2 区域可用性
在使用第三方云服务托管应用程序时,无法控制其基础设施,如果出现故障,应用程序将受到影响。多区域部署可以解决这个问题,因为云服务的停机通常会影响整个地理区域。通过将部署复制或分散到多个区域,可以降低地理问题对应用程序的影响。
多区域部署有两种常见模式:
| 部署模式 | 优点 | 缺点 |
| — | — | — |
| 复制(Replicated) | - 部署更简单,所有内容作为一个整体部署。
- 只需要一个负载均衡器根据可用性选择正确的区域。
- 客户端与平台的通信更简单。 | - 部署和负载均衡策略的灵活性较低。
- 如果使用内存会话,需要配置粘性会话或使用公共数据库。 |
| 分散(Spread) | - 在区域相关的部署方面具有更大的灵活性。
- 更强的容错能力,如果一个 API 失败,不会影响当前事务。 | - 部署计划比复制模式更复杂。
- 客户端与平台的通信更复杂,可能需要为每对 API 配置一个负载均衡器。 |
3.2 实际部署工具
在将代码部署到生产环境(或其他环境)时,可以使用一些工具来帮助上传代码并管理生产中的进程。以下介绍两种工具:
3.2.1 Shipit
Shipit 是一个用于自动化部署的工具,它可以帮助你将代码上传到服务器。具体操作步骤如下:
1. 安装 Shipit:使用
npm install shipit-cli -g
全局安装 Shipit 命令行工具。
2. 初始化项目:在项目根目录下运行
shipit init
初始化 Shipit 配置。
3. 配置 Shipit:编辑
shipitfile.js
文件,配置服务器信息、部署路径等。
4. 执行部署:运行
shipit staging deploy
(假设部署到 staging 环境)进行部署。
3.2.2 PM2
PM2 是一个进程管理器,可以帮助你在生产环境中管理应用程序的进程。具体操作步骤如下:
1. 安装 PM2:使用
npm install pm2 -g
全局安装 PM2。
2. 启动应用程序:在项目根目录下运行
pm2 start app.js
启动应用程序。
3. 管理进程:使用
pm2 list
查看进程列表,
pm2 restart app
重启应用程序,
pm2 stop app
停止应用程序等。
4. 总结
本文介绍了 API 测试的基础知识,包括如何编写异步测试用例、测试 BookSales 控制器的
create
方法,以及测试的执行和结果分析。同时,还探讨了不同环境下的开发流程,包括开发环境、质量保证环境、用户验收测试环境和生产环境的特点和作用。最后,讨论了生产环境的高可用性和实际部署工具,如负载均衡器、区域可用性、Shipit 和 PM2。通过合理使用这些技术和工具,可以提高 API 项目的质量和可靠性,确保应用程序在生产环境中稳定运行。
超级会员免费看

被折叠的 条评论
为什么被折叠?



