【发布时间】:2014-09-04 04:47:13
【问题描述】:
我正在重构一个中型 Web 应用程序,其中包含许多使用 RSpec 编写的单元测试和验收测试。我的主要目标是清理在生产中作为单独的机架应用程序运行的微服务的代码。当前的测试套件使用进程管理来启动和停止微服务,这会导致测试性能出现问题,并使测试环境管理测试夹具数据的方式变得复杂。
有数百个测试依赖于微服务的正确行为,这意味着在这个重构过程中,我将没有机会完全模拟或伪造微服务本身。但我真的很想删除所有单独的流程处理和相关成本。现在这是可行的,因为我正在为微服务本身添加单元测试,并且我们有一个单独的烟雾和集成测试基础架构,该基础架构已经与作为单独的基于机架的服务处理的微服务一起运行。
我创建了一个SSCE 来演示这个问题。它位于多个小文件中,功能齐全并包含测试。为整体长度道歉,但我认为这是不可避免的,因为我的问题是关于如何组合这些多个组件。
微服务
微服务/app.rb
require "grape"
class Microservice < Grape::API
format :json
get '/main' do
{ :message => "Hello World!" }
end
end
microservice/config.ru
require File.join(File.dirname(__FILE__), "app")
run Microservice
microservice/spec/spec_helper.rb
require_relative '../app.rb'
require 'rspec'
require 'rack/test'
def app
Microservice
end
RSpec.configure do |config|
config.include Rack::Test::Methods
end
microservice/spec/microservice_spec.rb
require_relative 'spec_helper'
describe Microservice do
describe "GET /main" do
it "responds with correct JSON" do
get "/main"
expect( last_response ).to be_ok
data = JSON.parse( last_response.body )
expect( data ).to be == { "message" => "Hello World!" }
end
end
end
主网络应用程序
webapp/app.rb
require 'sinatra'
require 'json'
require 'httparty'
# This stands in for more complex config we have in reality
$microservice_url = 'http://127.0.0.1:8090/'
get '/main' do
# Calls a microservice . . . (annoyingly the service uses the same route name)
response = HTTParty.get( $microservice_url + "main" )
data = JSON.parse( response.body )
"#{data['message']}\n"
end
webapp/spec/spec_helper.rb
require_relative '../app.rb'
require 'rspec'
require 'rack/test'
def app
Sinatra::Application
end
RSpec.configure do |config|
config.include Rack::Test::Methods
end
# Is there something I can do here to load the
# microservice without launching it in a new process, and route
# the HTTParty.get in the main app to it?
$microservice_url = 'http://127.0.0.1:8888/'
webapp/spec/webapp_spec.rb
require_relative 'spec_helper'
describe "Main web app" do
describe "GET /main" do
it "responds with correct text" do
get "/main"
expect( last_response ).to be_ok
expect( last_response.body ).to include "Hello World!"
end
end
end
我可以轻松运行微服务测试:
rspec -f d -c microservice/spec/microservice_spec.rb
但是要运行 webapp 测试,我首先需要启动测试期望找到它的微服务:
rackup -p 8888
(不同的过程)
rspec -f d -c webapp/spec/webapp_spec.rb
我想我可以通过查看How to mount a Sinatra application inside another Sinatra app? 之类的问题将微服务安装在应用程序中,但这似乎适合在生产环境中加入应用程序,我需要它们在那里分开,只在助手启用的单元测试中加入它,到目前为止,我完全不知道如何告诉 HTTParty(或任何可以在这里做我想做的事情的 replacememt)进行连接。
这是我在示例中对单个调用进行存根的方式(在 webapp/spec/spec_helper.rb 的末尾) - 有没有办法将其路由到微服务的进程内挂载代替?
require 'webmock/rspec'
include WebMock::API
stub_request( :any, /:8888\// ).to_return( :body => '{ "message":"Hello World!"}' )
【问题讨论】:
-
用VCR记录实际的网络流量怎么样?记录流量后,您的测试将在不触及微服务的情况下运行(因此您不必启动它)。
-
@Stefan:我认为这将成为所有灯具之母,非常难以管理,非常脆弱。它可能是将来启动模拟/存根过程的有用方法(即,如果我有时间处理每个小型网络交互并将其分配给它的测试)
-
@Stefan:我还要感谢您提供 VCR 的链接 - 我可以在主应用程序中看到它可能非常有用的其他一些地方。
-
我不太确定你想要达到什么目的。您是否正在寻找一种在运行测试时自动启动和停止微服务的方法?
-
@Stefan:有点。我已经知道如何将其作为一个单独的进程执行,以及如何在允许本地连接的同时运行
webmock。这就是当前测试所做的。但是以这种方式运行它有很多开销。我想在相同的rack/test环境中启动微服务,并在单个进程中运行所有内容,只需通过 Rack 接口将微服务请求发送到正确的路由处理程序,就像rack/test已经对被测单元所做的那样。