gpt4 book ai didi

mysql - 带有 Typescript 和 Mysql 的 Node.js 测试路由模拟服务

转载 作者:可可西里 更新时间:2023-11-01 08:59:54 25 4
gpt4 key购买 nike

我正在使用带有 typescript 的 node.js 来构建一个 web api。对于数据库,我使用的是 mariadb (mysql)。我已经使用存储库模式 + 工作单元构建了项目(我不是 .NET 开发人员,但我在一家使用 .NET 的公司工作)。 我正在尝试编写测试,到目前为止,我已经能够使用 chai 和 typemoq 库为我的模拟为域模型和服务编写它们。我想对于存储库也是可行的。我面临问题的地方(目前)是在我使用我的服务的路线上。是否可以测试路由并模拟服务?我看过很多 Node 教程,他们在其中为路由编写测试,但他们只是检查响应而没有模拟任何东西。这不是类似于集成测试而不是单元测试吗?有人会如何将 TDD 应用于 Node ?

这是一个例子:

router.post('/register', (req, res, next) => {

try {
let newUser = <RegisterUserDTO>req.body;
let uow = <IUnitOfWork>req.uow;
let userRepository = new UserRepository(uow);
let userService: IUserService = new UserService(userRepository);

userService.getByUsername(newUser.username)
.then((user:User) => {

if(!user) {
userService.getByEmail(newUser.email).then(user => {
if(!user) {
userService.insertUser(newUser)
.then((result) => {
res.json({
success: true,
msg: "User registered",
userId: result.insertId
});
})
.catch(err => {
res.json({ errorMessage: err.message, errorStackTrace: err.stack });
});
}else {
res.json({ success: false, msg: "This email already exists" });
}
});
}else {
res.json({ success: false, msg: "This username already exists" });
}
})

}
catch(err) {
res.json({ errorMessage: err.message, errorStackTrace: err.stack });
}

});

我将如何模拟 userService 并测试这条路线?这是使用 Node 的“错误”方式吗?

提前致谢

最佳答案

为了使测试更容易,我会在您的服务中加入更多逻辑,并划分路由和服务的职责。在你的路由中有很多 if 语句甚至一个存储库创建将使测试变得非常困难,并赋予路由比仅仅路由请求和响应更大的责任。

这是构造它的一种方法。

user.route.js

const userService = require("../services/user.services.js")

router.post("/register",function(req){
var potentialUser = webAdapter.getUserFromRequest(req)
var response = userService.register(potentialUser) //returns a promsie
webWriterUtility.json(response)

UserRoute的职责

如果您分析上面的代码,路由的职责不再是确定适当的响应消息、检查用户是否存在等。它的工作是从 userService 获取响应并将其发送给用户.这简化了我需要测试的内容。因为在路由测试中我只关心路由内立即发生的事情,所以我可以完全去掉 userService.register。 userService 是否返回 promise resolve 或 promise rejection 并不重要,重要的是路由正确处理了这些场景。

上面的代码和 URL“/user/register”可以很容易地用 sinon.js 进行测试。 Node 只加载一次“必需”模块,因此在您的用户路由单元测试中,您可以加载 userService,将 stub 添加到注册函数,然后当 UserRoute 加载 userService 时,它​​会获取 stub 函数。

var registerStub = sinon.stub(userService,"registerUser") 
registerStub.callsFake(()=>{
return Promise.resolve(userResponse)
})

这是一个使用 stub 方法的用户路由测试示例

user.routes.test.js

const superTest   = require('supertest')
const expect = require('chai').expect
const assert = require("chai").assert
const sinon = require("sinon")
const userService = require("../services/user.services")
const app = require("../app').app

describe("User Routes: ",function(){
var registerStub = sinon.stub(userService,"registerUser")

beforeEach(()=>{
registerStub.callsFake(()=>{
return Promise.resolve(userResponse)
})
})
afterEach(()=>{
registerStub.restore()
})

it("can get register a user",function(done){
superTest(app).post("/user/register")
.set("XXXXXXX")
.send("email=sfsfsf&firstName=xsdfsfs")
.expect(200).then( response =>{
assert(response.text == EXPECTED_RESPONSE)
done()

}).catch(e=>{
//TEST FAILED
console.log(e)
done(e)
})
})
})

上面的测试是一条绊线,确保用户路由充当 URL“/user/register”上的事件处理程序。它不是对 userService 内部逻辑的测试。 UserRoute 和 UserService 在此设计中有 2 个独立的职责。

UserService 的责任

UserService.register 返回解析为响应对象或拒绝错误对象的 promise 。如果 promise 是拒绝,另一个实用程序类负责将该结果转换为状态代码 500,如果 promise 已解决,则负责将结果转换为 JSON。可以使用早期的 stub 方法来测试下面的代码,以模拟您的数据库客户端/模块,使其看起来像用户已经存在,然后确认返回了适当的消息。

user.serivice.js

    module.exports.register = function(req){
var resolve;
var reject;
var promise = new Promise((resolveTemp,rejectTemp) => {
resolve = resolveTemp
reject = rejectTemp
})

let newUser = <RegisterUserDTO>req.body;

getByUsername(newUser.username)
.then((user:User) => {
if(!user) {
getByEmail(newUser.email).then(user => {
if(!user) {
insertUser(newUser)
.then((result) => {
resolve({
success: true,
msg: "User registered",
userId: result.insertId
});
})
.catch(err => {
reject(err);
});
}else {
resolve({ success: false, msg: "This email already exists" });
}
});
}else {
resolve({ success: false, msg: "This username already exists" });
}
})

return promise
}

您可以通过将操作结果包装为 promise 来分离用户服务对请求和响应的了解需求。对我来说,错误对象会导致一切中止,因此 reject(err) 通常就足够了,但您可以使用 reject({message:"ssfsfs",code:500,error:err}) 或创建一个 ApplicationError 用于所有拒绝的类型。这是一种让错误从业务逻辑冒泡到 HTTP 层的巧妙方法,而无需将 Request 和 Response 对象深入传递到您的业务逻辑中。

关于mysql - 带有 Typescript 和 Mysql 的 Node.js 测试路由模拟服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48146006/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com