草庐IT

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

coder 2023-10-26 原文

我正在使用带有 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/

有关mysql - 带有 Typescript 和 Mysql 的 Node.js 测试路由模拟服务的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  3. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  4. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  5. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  6. ruby - Ruby 的 Hash 在比较键时使用哪种相等性测试? - 2

    我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。

  7. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  8. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  9. ruby - Sinatra:运行 rspec 测试时记录噪音 - 2

    Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/

  10. ruby-on-rails - 启动 Rails 服务器时 ImageMagick 的警告 - 2

    最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru

随机推荐