【问题标题】:Running a JS test suite from a Rails environment with a fixture factory, or an API for frontend to request certain fixtures to be loaded on backend从带有夹具工厂的 Rails 环境中运行 JS 测试套件,或者前端的 API 以请求将某些夹具加载到后端
【发布时间】:2015-05-18 16:28:32
【问题描述】:

我是一名使用 EmberJS 的前端开发人员。这是一个很棒的前端框架,它采用了 Rails 的许多优点:它非常复杂和固执己见,同时它非常灵活且易于使用。

Ember 拥有自己的测试套件,完全能够进行验收测试。但是它要么针对后端模拟进行测试,这有两个原因很糟糕:创建功能性后端模拟很乏味,并且它不测试前端-后端集成。或者 Ember 可以针对正常运行的后端进行测试,这使得无法使用像 FactoryGirl/Fabrication 这样的夹具工厂,并且您必须在每次测试后手动重置测试数据库。

对此的传统解决方案是使用 Capybara。 Capybara 的问题在于 Ember 本质上是非常异步的。 Capybara 无法跟踪 Ember 是否已完成请求数据/计算反应属性/渲染 GUI 等。

有些人使用 Capybara 来测试 Ember,他们都使用最丑陋的 hack。这里只是给你几个让你尝尝它的恶臭:

patiently do
  return unless page.has_css? '.ember-application'
end

2000.times do #this means up to 20 seconds
  return if page.evaluate_script "(typeof Ember === 'object') && !Ember.run.hasScheduledTimers() && !Ember.run.currentRunLoop"
  sleep 0.01
end

source

def wait_for_ajax
 counter = 0
 while true

 active = page.execute_script(“return $.active”).to_i
 #puts “AJAX $.active result: “ + active.to_s

 break if active < 1
 counter += 1
 sleep(0.1)
 raise “AJAX request took longer than 5 seconds OR there was a JS error. Check your console.” if counter >= 50
 end
end

source

所以这个想法是使用美妙的 Ember 的 JS 测试套件来执行 来自 Rails 的测试。它将允许使用 Factory Girl 为每个测试专门设置数据库......并运行 NodeJS/Ember 测试而不是 Capybara。

我可以想象这样的事情:

describe "the signin process", :type => :feature do
  before :each do
    FactoryGirl.create(:user, :email => 'user@example.com', :password => 'password')
  end

  it "signs me in" do
    test_in_ember(:module => 'Acceptance: User', :filter => 'signs me in')
  end

  it "logs me out" do
    test_in_ember(:module => 'Acceptance: User', :filter => 'logs me out')
  end
end

Ember 有一个用于测试的命令行界面,可以运行基于Testem 的特定测试。

或者,Ember 测试套件可以正常使用,但它会通过调用特殊 API 开始每个测试,请求加载某些夹具工厂定义。

正式问题:我如何同时使用两全其美的优点:EmberJS 的原生测试套件和 Ruby on Rails 的原生夹具工厂?

【问题讨论】:

  • 您可以将固定装置转储到 JSON 文件中。但它们仍然需要由某种服务器提供服务,以避免file:// 协议和 CSR 的问题。
  • @papirtiger,这毫无意义。我最初的目标是测试前端-后端集成,动态测试真正的后端逻辑,而不是一些静态 API 模拟。
  • FactoryGirl 是一个用于创建工厂的 Ruby DSL - 每个工厂定义只是一个 ruby​​ 代码块,而不是一些静态定义。如果你想移植工厂的动态方面,例如序列等,你必须编写一个解析器,它可以转译成 javascript。

标签: ruby-on-rails ember.js capybara integration-testing factory-bot


【解决方案1】:

有很多问题:

  1. FactoryGirl 是一个用于创建工厂的 Ruby DSL - 每个工厂定义只是一个 ruby​​ 代码块,而不是一些静态定义。如果您想移植工厂的动态方面(例如序列等),则必须编写一个解析器,将 DSL 转换为 javascript DSL。
  2. 无论如何,如果不访问您的公共 API,Javascript 工厂将无法写入数据库。

关于如何桥接的一些粗略想法:

  1. 在您的规范之前设置固定装置 - 并将它们传递给您的脚本。大多数 javascript 驱动程序允许您使用 page.execute_script 可用于传递序列化夹具。缺点:必须在测试执行开始之前完成。

  2. 设置工厂 API,允许您通过 ajax 调用工厂。最好在可安装的 Rails 引擎中。缺点:慢、异步


def FactoriesController
   # get /spec/factories/:factory/new
   def new
     @factory = FactoryGirl.new(params[:factory_uid])
     respond_to do |f|
       render json: @factory
      end
   end

   # post /spec/factories/:factory
   def create
     FactoryGirl.create(params[:factory_uid], factory_params)
   end

   # ...
end
  1. 创建一个仍无法解决问题 2 的 FactoryGirl 解析器。

补充:

一个糟糕的示例工厂客户端:

var FactoryBoy = {
    build: function(factory, cb){
       $.ajax('/factories/' + factory + '/new' , dataType: 'json').done(function(data){ cb(data) });
    }, 
    create: function(factory, cb){
        $.ajax('/factories/' + factory , dataType: 'json' method:'POST').done(function(data){ cb(data) });
    }
}

测试示例:

module('Unit: SomeThing');

test('Nuking a user', function() {
  var done = assert.async(); 
  FactoryBoy.create('user', function(user){
     this.store.find('user', user.id).then(function(u){
       $('#nuke_user').click();
       ok(u.get('isDeleted'));
       done();
     });
  });
});

【讨论】:

  • 谢谢你,但你还是没抓住重点,papirtiger。我有一个包含大量业务逻辑的 Rails 后端。当你使用前端时,它会导致后端在后台做很多事情。我想测试一下。仅仅生产固定装置并将它们提供给前端是不够的。我希望将固定装置加载到数据库中,以便后端功能齐全并处于某种状态。问题是每个测试都需要一个新的(并且是唯一的!)初始状态,这意味着必须为每个测试单独加载一组不同的夹具到后端
  • 不,请再次阅读答案。我的两个建议都涉及持久化工厂。第一个解决方案将要求您在运行 javascript 测试之前设置该特定测试所需的任何固定装置。第二个使用普通的旧 REST api 从您的 javascript 测试中创建工厂。将此与 DatabaseCleaner 结合使用,以在每个规范之后擦除测试数据库。
  • 好的,所以我们正在讨论让后端在测试模式下运行并为每个测试填充固定装置。在我的问题中,我概述了两种可能的方法:1)从 Rails 开始测试,正常填充固定装置,然后以某种方式执行 JS 测试并收集报告; 2) 从 JS 开始测试,使用一些特殊的 API 来告诉后端在每次测试之前要加载哪些夹具。您的第二个解决方案似乎与我的第二个相同。唯一的区别是我不需要通过 GET 来提供固定装置,我只需要告诉后端要加载哪些固定装置,然后通过普通 API 使用后端。
  • 但是我仍然需要关于第二种方式的实现细节。如何使控制器仅在测试模式下可用?如何在使用新设备填充数据库之前清除旧数据?等等。如果您详细说明并且我设法在样板 Rails 应用程序中重现它,我将很乐意接受您的回答。
  • 至于您的其余回答,我们似乎仍然存在误解。 “无论如何,如果不访问您的公共 API,Javascript 工厂将无法写入数据库。” -- 我确实希望前端在测试期间使用普通的 API。这就是集成测试的重点。
猜你喜欢
  • 1970-01-01
  • 2010-09-18
  • 2011-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-12
  • 2020-05-25
  • 1970-01-01
相关资源
最近更新 更多