- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我正在使用带有 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/
我的 Angular 应用程序中有以下代码。 app.config(function($routeProvider, $locationProvider) { $locationProvider
这就是我在 Backbone 中进行路由的方式,在决定调用哪个外部模板之前,首先获取路由及其参数。我觉得这很灵活。 var Router = Backbone.Router.extend({
我是 MEAN 堆栈领域的新手,我对 Angular 路线有一些疑问。为什么我应该在客户端重新创建后端已经用express.js创建的路由,有什么好处?这是 Angular.js 工作的唯一方式吗?我
我可以设置一条从根级 URL 进行映射的路由吗? http://localhost:49658/ 我使用的是 VS2010 内置 Web 服务器。 尝试使用空白或单斜杠 URL 字符串设置路由不起作用
我有一个现有的应用程序 Rails 3.2.17和 Angular js。我想在现有应用程序中包含 Activeadmin。 我遵循了 active-admin post from ryan bate
我正在关注 this Angular 中的路由教程,它就是行不通。当我使用“comp”选择器放置它的 HTML 代码时,它可以工作,但是当我尝试使用路由器 socket 对其进行路由时,它只显示来自
多个路由通过路由器进行管理。 前端路由的概念和原理 (编程中的) 路由 (router)就是一组 key-value 对应关系,分为:后端路由和前端路由 后端路由
服务器需要根据不同的URL或请求来执行不一样的操作,我们可以通过路由来实现这个步骤。 第一步我们需要先解析出请求URL的路径,我们引入url模块。 我们来给onRequest()函数加上一些逻辑
我正在为 Angular 6 应用程序设置路由,我想要一条可以匹配可变数量的段的路由。目前我有一个看起来像这样的路由配置: const routes: Routes = [ { path: '',
用户将点击电子邮件中的链接,如下所示: do-something/doSomething?thing=XXXXXXXXXXX 如何在路由器中定义路由并订阅获取参数? 目前在我的路由器中: {
我有一个具有以下结构的 Angular (4) 应用程序: app.module bi.module auth.module 路由应该是: / -> redirect to /home /
我正在使用 WCF 4 路由服务,并且需要以编程方式配置服务(而不是通过配置)。我见过的这样做的例子很少见,创建一个 MessageFilterTable 如下: var fi
我需要创建一个“路由”服务。我正在尝试使用 .Net 的 System.ServiceModel.Routing.IRequestReplyRouter我可以让它只在 HTTP 模式下工作,而不是在
例如,链接: /shop/phones/brend/apple/display/retina/color/red 在哪里: phones - category alias brend -
非常基本的问题,我很惊讶我找不到答案。我刚刚开始研究 django 并进行了开箱即用的安装。创建了一个项目并创建了一个应用程序。 urls.py 的默认内容很简单: urlpatterns = [
我已经实现了 WCF 路由服务;我还希望该服务(或类似的 WCF 服务)以规定的和统一的(与内容无关的)方式转换有效负载。例如,有效负载将始终采用 Foo 的形式。我想把它作为Bar在所有情况下。我很
我想使用 $locationProvider.html5Mode(true); 在 angularJs 中删除 # 哈希;但这导致所有 URL 都通过 angularJs 进行路由。我如何设置它以便只
我要听导航开始事件并判断其是否url属性是 /logout . 如果是这样,路由器应该停止触发连续事件,例如 路线已识别 , GuardsCheckStart , ChildActivationSta
有人可以解释我如何使用参数路由到 URL 吗? 例如id 喜欢点击产品并通过Id打开产品的更多信息。 我的路由到目前为止... angular.module('shop', ["cus
我目前正在 Angular: 7.2.14 上构建,想看看是否有人可以解释如何使用路由保护、共享服务或其他方式等重定向查询参数。 我试图解决的问题要求查询参数从根 Uri 路径传入,然后将路由重定向到
我是一名优秀的程序员,十分优秀!