Rails 在一个简单项目中探索 Rails 5.1 新功能:yarn 集成和集成测试

ericguo for 巴爷科技 · April 16, 2017 · Last by huobazi replied at April 30, 2017 · 7455 hits
Topic has been selected as the excellent topic by the admin.

全文也在我的博客有发布,欢迎在保留原文链接的前提下进行转载。

Rails 5.1 即将发布,虽然同 Rails 5 相比,这只是一个 0.1 的小版本,但我看来,Rails 5.1 的新的功能却非常重要:在 JavaScript 世界建构和模块管理工具突飞猛进了 3~4 年后(grunt, gulp, browserify, npm, webpack, yarn),一向喜欢稳定的 Rubyist 们(喜欢折腾的都跑去 javascript, go 和 Elixir 了。。),在大神DHH和女神Liceth以及其他众神的帮助下,迎来了和 node.js 国的最终和解:YarnWebpack已经正式成为了主厨推荐

本文和其他介绍Rails 5.1 Whatsnew稍有不同的地方在于,我试图通过一个新的项目探索 Rails 5.1 的特点。

product hunt是一个功能简单的 Rails 应用,仅仅实现了产品的维护,但在功能方面,探索了 yarn 下对新 javascript 框架的使用,还有集成测试。

具体来说,使用了比较小众的milligram CSS 框架,输入自动提示使用了零依赖的awesomplete,使用基于 Chrome 的集成测试。

Yarn

Yarn 同 npm 相比,优点很多,yml 格式的 yarn.lock 文件显示依赖关系清晰易读,安装速度快,完全不需要再使用 asset pipe line 对已有的 javascript 模块进行二次封装,省事省力,省开新 gem。环境准备也相当容易,在国内(必须)用淘宝源即可。

brew install yarn
yarn config set registry 'https://registry.npm.taobao.org'

设置完毕后,就可以直接使用 yarn 添加依赖了。

yarn add milligram

使用 CSS 前端框架,直接引用即可:

/* app/assets/stylesheets/application.css */
*= require milligram
// app/assets/stylesheets/milligram.sass
@import milligram/src/Color
@import milligram/src/Base
@import milligram/src/Button

使用 javascript 库多做一步:

// app/assets/config/manifest.js
//= link awesomplete/awesomplete.js
<%# app/views/products/_form.html.erb %>
<%= javascript_include_tag "awesomplete", async: true -%>

Webpack

Webpack 是可选的,如果是majestic monolith的 Rails 应用的话,肯定不会用,但是现代的前后端分离风潮下,至少 Rails 5.1 让这个前后端分离变得无比容易,比之前的react_on_rails方案容易好多。

jQuery 的依赖去除

由于 Javascript 一众 MVC 框架的崛起,Rails 5.1 在前端方案上给予了开发者更多的选择权,我试了在没有 jquery 的情况下,使用 0 payloading 的vanilla-js开发,虽然时不时还需要查一下对应 jQuery 的用法,但这样做还是值得的,微信小程序的卖点就是即用即走,但如果能将网页做到轻量,何尝不是即用即走?况且网页发布还不用走审核流程。

在 Rails 下使用 vanilla js 其实也不是什么都没得用,rails-ujs始终是存在的,所以还是可以直接使用 Rails.ajax 方法来发起远程调用:

window.addEventListener('input', function (e) {
  if (e.target.id == "product_name") {
    Rails.ajax({
      type:'GET',
      url: e.target.getAttribute('data-url'),
      dataType: 'script'
    });
  }
}, false);

System Test

Rails 5.1 对集成测试也有了官方方案,现在出厂即可跑集成测试。集成测试在测试自动提示这样的特性的时候还是很方便的,官方出厂的默认配置基于 selenium-webdriver 驱动的 Chrome,使用之前需要安装驱动:

brew install chromedriver
require 'application_system_test_case'

class NewProductTest < ApplicationSystemTestCase
  test 'create a new product' do
    visit '/products/new'

    fill_in 'product_name', with: 'L'
    page.has_selector?('ul > li > mark')
    fill_in 'product_name', with: 'Le Wagon'
    page.has_no_selector?('ul > li > mark')
    fill_in 'product_tagline', with: 'Change your life: Learn to code'
    click_button 'Create Product'

    # Should be redirected to Home with new product
    assert_equal product_path(Product.last), page.current_path
    assert page.has_content?('Change your life: Learn to code')
  end
end

这里第一次在 product_name 填入'L',应该有自动完成的提示出现,完全输入后,则没有,这些都可以通过 Capybara 的浏览器 DSL 做检测,从而在集成测试中一并测试好,相比之前的单个 controller 测试,效率提高不少。

其他特性。

其他的特性:Encrypted secrets、Parameterized mailers、Direct & resolved routes 等,参见Rails 5.1 Release notes,演示项目中没有包括这些功能,这里就略过了。

本文不足

测试方案还想使用phantomjs 和 poltergeist,但是始终没有成功,有兴趣的同学可以尝试并提 Pull Request。

jasl mark as excellent topic. 17 Apr 05:39

早上看新闻说因为 Chrome 59 开始支持 headless 模式,PhantomJS 宣布弃坑。 https://groups.google.com/forum/m/#!topic/phantomjs/9aI5d-LDuNE

yarn 比之前的 rails-assest 靠谱多了。👍

另外,我这里测试 yarn 不换源也非常快。就是每次运行 yarn 都要 rebuild node-sass,自己手动用 webpack 搭建并没有这个问题

我直接 revert 8f5df2358e7ee9c9a65d0412e0299e4f14659564 这个 commit,集成测试是可以跑过的。配置应该没问题。我猜可能是 phantomjs 版本的问题,我本地的是

phantomjs --version
2.1.1

另外 selenium 和 poltergeist 方法有一些的不同,同时使用(我们一个用在本地调试,一个用于 CI),略蛋疼,有想解决办法,但一直也没时间去搞。

同时建议使用screen_size: [1400, 1400],因为屏幕太小的话,会有因为点不到东西(元素不在显示器内)而报错的时候。

Reply to yfractal

我也是 phantomjs 2.1.1,我会报这个错,虽然可以加page.driver.browser.js_errors = false,单感觉这办法不好。

Error:
NewProductTest#test_create_a_new_product:
Capybara::Poltergeist::JavascriptError: One or more errors were raised in the Javascript code on the page. If you don't care about these errors, you can ignore them by setting js_errors: false in your Poltergeist configuration (see documentation for details).

TypeError: undefined is not an object (evaluating 'handler.call')
TypeError: undefined is not an object (evaluating 'handler.call')
    at http://127.0.0.1:52934/assets/application-98eeb46022232c75bc6d2971073ca25d12383cefe13cc520805de10f77a13424.js:145
    at :0 in sendEvent
    at phantomjs://code/web_page.js:59
    at phantomjs://code/web_page.js:558 in mouseEvent
    at phantomjs://code/node.js:67 in mouseEvent
    at phantomjs://code/browser.js:398 in mouse_event
    at phantomjs://code/browser.js:418 in click
    at phantomjs://code/browser.js:89 in runCommand
    at phantomjs://code/cmd.js:35 in run
    at phantomjs://code/main.js:17 in runCommand
    at phantomjs://code/connection.js:16 in commandReceived
    at phantomjs://code/connection.js:1
TypeError: undefined is not an object (evaluating 'handler.call')
TypeError: undefined is not an object (evaluating 'handler.call')
    at http://127.0.0.1:52934/assets/application-98eeb46022232c75bc6d2971073ca25d12383cefe13cc520805de10f77a13424.js:145
    at :0 in sendEvent
    at phantomjs://code/web_page.js:59
    at phantomjs://code/web_page.js:558 in mouseEvent
    at phantomjs://code/node.js:67 in mouseEvent
    at phantomjs://code/browser.js:398 in mouse_event
    at phantomjs://code/browser.js:418 in click
    at phantomjs://code/browser.js:89 in runCommand
    at phantomjs://code/cmd.js:35 in run
    at phantomjs://code/main.js:17 in runCommand
    at phantomjs://code/connection.js:16 in commandReceived
    at phantomjs://code/connection.js:1
    test/integration/new_product_test.rb:12:in `block in <class:NewProductTest>'
Reply to ericguo

poltergeist问题很邪乎,经常是有的机器上有问题,有的没有。

我本地跑了下,似乎是用到了 CSS Animations,可以先试试直接禁用掉(很多都这么干),具体可以参考这个链接

还可以点击事件换成find('#xxx').trigger('click'),这个是不管是否有遮挡,都能 click。

Reply to huobazi

歪个楼,@huobazi 的七牛文件直穿好高级!有个 PR 能收一下么?

Reply to ericguo

感谢🙏,现在在外面,明天回去了 merge

You need to Sign in before reply, if you don't have an account, please Sign up first.