我得 Ruby 最秀的地方(RSpec) - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
mizuhashi
V2EX    程序员

我得 Ruby 最秀的地方(RSpec)

  •  1
     
  •   mizuhashi 50 天前 3388 次点击
    这是一个创建于 50 天前的主题,其中的信息可能已经有所发展或是发生改变。

    看隔壁於 ORM 的 Ruby/Rails 的都被其他言走了,但我得至少 RSpec 在 JS 世界有替代。以下是一用 RSpec 的例子:

    require 'rspec' require 'matrix' RSpec.describe 'numbers' do # 描述加法的性 shared_examples 'addition' do # 加法足交律 # a 和 b 在的 context 注入 it 'has commutativity' do expect(a + b).to eq(b + a) end end context 'for number' do # 令 a 和 b ,用加法的用例 let(:a) { Random.rand } let(:b) { Random.rand } it_behaves_like 'addition' end context 'for vector' do # a 和 b 可以是向量,也足加法的性 let(:a) { Vector[Random.rand, Random.rand] } let(:b) { Vector[Random.rand, Random.rand] } it_behaves_like 'addition' end end 

    如果要用 js ,至少所有的 a 和 b 都要成似context.a context.b,因 ruby 有 self ,而 js 只有法作用域,致 js 的 dsl 表力很受限。

    另外在 ruby 也可以很易做 mock ,例如例子修改了Date.today返回我用的日期:

    require 'rspec' require 'date' RSpec.describe 'Date' do # 定一用的日期,一般使用的 let(:date) { Date.parse('1970-01-01') } before do # Date.today 本身是定好的,但我可以覆它返回我的日期 allow(Date).to receive(:today).and_return(date) end describe '.today' do it 'is the mocked date' do # Date.today 在是我 mock 的日期 1970-01-01 expect(Date.today).to eq(date) end end end 
    33 条回复    2025-08-22 22:36:01 +08:00
    Ketteiron
        1
    Ketteiron  
       50 天前   2
    ror 最优秀的地方在于极致地实现约定大于配置,以及魔法般的 DSL 。
    有什么好处?就是极致地少写代码。

    不同人有不同的喜好,就像我追求的是接近完美的类型安全,多写代码无所谓,反正现在大部分活是 AI 干的。而类型安全让我几乎杜绝各种傻逼运行时错误,可以安心地睡觉。你要说人菜那我无话可说,我本来就是一名普通的程序员,一定会犯错误,我只能希望能少犯错误。Ruby 很优秀与 Ruby 不适合团队协作并不冲突。
    ericguo
        2
    ericguo  
       50 天前   1
    @dssxzuxc 多写代码费 token 啊。。。能少写一点总是好的,只要没有歧义,我觉得 ruby 甚至可以当作以后所有其他语言的 LLM 转换的源语言用。没有明面上的类型干扰,更加容易让 LLM 关注逻辑,从逻辑推断出各个语言应该使用的类型或者需要导入的库。
    mizuhashi
        3
    mizuhashi  
    OP
       50 天前
    @dssxzuxc 那 idris 估最符合,不型安全,甚至能用型明正
    NineTree
        4
    NineTree  
       49 天前
    看不懂,但是感觉很厉害
    qiumaoyuan
        5
    qiumaoyuan  
       49 天前
    @dssxzuxc 我觉得不是适合不适合团队合作,而是适合什么样的团队合作。我还是那句话,程序员写代码时的思维应该是主动的、清醒的、活跃的,而不是被动的、怠惰的。当然如果你习惯把一个方法的代码写得超过 10 行,那确实大脑容易超载。
    visper
        6
    visper  
       49 天前
    ruby 都已经没人用了吧。
    dddd1919
        7
    dddd1919  
       49 天前   2
    @dssxzuxc #1 除此之外觉得还有一点就是 all in one 的思路,通过这种 add-in 可以在一个工程内解决各种各样的问题。测试除了 Rspec ,还用过 Capybara 处理模拟 web 的测试,用 factory girl 构造铺底数据,用 waiter 去做自动化灰盒,这一切都是一个工程统统搞定,到现在其他生态很难找到这么完整的工具生态
    catamaran
        8
    catamaran  
       49 天前
    @qiumaoyuan 如果你的代码每个方法都不超过 10 行,我想可以称为神人了
    kakki
        9
    kakki  
       49 天前
    这种手工造 DSL 的语言根本不适合有一定规模的团队合作,对于代码民工来说,架构设计为做填空题就行,无论谁来拉翔都破坏力有限,Ruby 比较适合小规模专家级团队,尤其是独立开发.
    qiumaoyuan
        10
    qiumaoyuan  
       49 天前
    @catamaran 不要自己吓自己,也不要限制自己。我觉得主要是很多人遇到问题习惯性的选择应付,而不是死磕。看看多数人对“屎山”的态度,几乎没有人选择去清理干净,增强自己能力的,而是能跑就行。在“屎山”的基础上,遇到 bug 的态度是兵来将挡水来土掩,不考虑怎么系统性的解决,除非实在应付不了,才会逼自己努力思考一下。
    zpvip
        11
    zpvip  
       49 天前
    @kakki 我一直搞不明白很多团队的代码合并流程, 团队成员发 pull request 时不同时提交测试吗? pull request 合并前没有人审核代码吗?

    如果新代码能通过各种测试, 有两人以上 approved, 能有什么破坏力?
    Configuration
        12
    Configuration  
       49 天前
    实际上 RSpec 在 ruby 社区争议挺大的,喜欢的人很喜欢,讨厌的人很讨厌
    cloudzhou
        13
    cloudzhou  
       49 天前
    说起这个测试用例,接触 ai 后,平时补充代码不让我惊讶
    ai 来写测试代码,那真是又稳又细,修修改改就可以用了

    所以 ai 目前还没有到创造性的时候,擅长于已有的数据下,分析进行后续处理
    kakki
        14
    kakki  
       49 天前
    @zpvip 因为很多公司的要求 1. 出活 2. 低成本的出活 招来低水平的人也要能干活 完全不是你说的技术性问题, 这本质上是一个资源管理问题.
    cloudzhou
        15
    cloudzhou  
       49 天前
    @zpvip #11 按照我经历过的公司,没有哪家真的测试用例 80% 以上的,都是主流程走一走,甚至依靠一些白盒测试,而人日压缩越发严重,压力很大,每次接需求就是填坑而已

    之前我从 Java 世界到 Django ,简直蜜月期
    后来为什么去掉幻想呢?从我需要大批量修改某个变量开始

    比如说有一个广泛大量使用的表,我因为业务需求,需要字段重命名 nameXXX -> nameYYY
    如果是静态语言,那么 ide -> refactor/rename 等,一把搞定
    同理 看某个全局变量哪里引用,对应修改逻辑

    我不知道这么多年,脚本语言是否改进了,在当时的话,是依靠 grep + 人肉

    但是,我修改了好多次,发现总是漏了一些地方
    起码 Python 来说,是运行时解析,到对应代码,才抛出错误

    ror 不知道是否完善一些
    msg7086
        16
    msg7086  
       49 天前
    @kakki #9 #14
    所以当年湾区初创公司一堆用 Rails 的,反观国内一堆用 PHP 的。
    其实就是你说的小规模专家级团队 vs 招一堆兼职大学生或者低水平的人。
    lithium4010
        17
    lithium4010  
       49 天前
    jest-plugin-set
    zpvip
        18
    zpvip  
       49 天前
    @cloudzhou #15

    Rails 的标准 CRUD 的 DB Migration, Controller, Model, View 还有 RSpec / Minitest 都是命令行生成的, 如果就真是标准的 Restful, 那测试就是 100% 覆盖且通过, 此时你还没有写一行代码, 只运行了一行命令, 例如:

    bin/rails generate scaffold Post title:string body:text

    具体什么过程, 可以去这看视频: https://kamal-deploy.org, 从 9:54 开始.

    我一般会把 View 模板选做好, 不像 DHH 演示那样阳春. 当然实际情况不是这么简单, 各种一对多, 多对多关系, Post 嵌套 Comments 之类的, 补充一些测试用例也非常简单, 另外会提前安装测试相关的 Gems, 比如:
    gem "capybara"
    gem "selenium-webdriver"
    gem 'webmock', '~> 3.25'
    gem 'shoulda-matchers'
    gem 'rspec-rails'
    gem "factory_bot_rails",
    gem "faker", "~> 3.5"

    现在有了 AI, 它自己会发现这些 Gems 并用这些 Gems 去写 RSpecs. 所以覆盖率 95% 以上是不难的.

    一般 Rails 程序员都会有个启动模板, 也可以用别人现成的.

    https://github.com/nickjj/docker-rails-example

    字段重命名的事, IDE 批量替换就可以了吧, 如果测试覆盖率好, 一下就可以找出漏网之鱼吧. 当然, 最简单的方法就是不改旧代码, 加个别名就可以了, 李白, 字太白, 号诗仙, 几个名字同时可用, 以后有心情了也可以一天改一处, 发现一处改一处, 天天提交 Git commit 还可以混 KPI.

    class User < ApplicationRecord
    alias_attribute :name_老字段名, :name_新改的字段名
    end

    u = User.new
    u.name_老字段名 = "Alice"

    u.name_老字段名 # => "Alice"
    u.name_新改的字段名 # => "Alice"

    u.name_新改的字段名 = "Bob"
    u.name_老字段名 # => "Bob"


    我以前做了十多年 C++, 副业也用 PHP 开发过流量很大的系统, 后来尝试换成 Ruby on Rails, 发现这才是网站开发的正确方式, 再后来发现全职 RoR 岂不是更爽, C++ 又不会给更多钱.

    年纪大了为了能更适应就业市场, 也尝试用 Go/C#/Java/PHP/JS (React, NodeJs), 越用越气, 相比 Ruby on Rails 真是太麻烦了, 稍写两下就感叹, 在 Ruby on Rails 不就一句话的事吗, 各种测试写得头痛, 难怪覆盖率上不去. 语法方面, Java 的 Lamda 把我气笑了, 只有一个抽象方法的接口?! 真是老气横秋, 感觉有人按着我的头写代码, 的确是很好的防呆设计.

    AI 时代, 现在程序员一个顶十个, 全栈工程师应该更吃香, 希望我还能再战几年后再去卖红薯.
    razertory
        19
    razertory  
       49 天前
    是不是用 Scala 写出来也优雅

    import org.scalatest.flatspec.AnyFlatSpec
    import org.scalatest.matchers.should.Matchers
    import org.scalatest.{OneInstancePerTest, Outcome}
    import org.scalatest.wordspec.AnyWordSpec
    import scala.util.Random

    // 共享测试
    trait AdditionBehaviors { this: AnyWordSpec with Matchers =>

    // 用 def 而不是 val ,让子类可以延迟绑定
    def a: Double
    def b: Double

    def additionBehavior(): Unit = {
    "addition" should {
    "be commutative" in {
    (a + b) shouldEqual (b + a)
    }
    }
    }
    }

    // 针对 Double 的测试
    class DoubleAdditionSpec extends AnyWordSpec with Matchers with AdditionBehaviors {
    override def a: Double = Random.nextDouble()
    override def b: Double = Random.nextDouble()

    "Double" should {
    behave like additionBehavior()
    }
    }

    // 针对 Vector[Double] 的测试
    import breeze.linalg.{DenseVector => Vec}

    class VectorAdditionSpec extends AnyWordSpec with Matchers with AdditionBehaviors {
    override def a: Double = Random.nextDouble()
    override def b: Double = Random.nextDouble()

    // 这里把 a 、b 换成向量
    private val v1 = Vec(a, a)
    private val v2 = Vec(b, b)

    "Vector" should {
    behave like additionBehavior()
    }

    // 重新实现加法行为,因为这里是向量
    override def additionBehavior(): Unit = {
    "addition" should {
    "be commutative" in {
    (v1 + v2) shouldEqual (v2 + v1)
    }
    }
    }
    }
    charles0
        20
    charles0  
       49 天前
    QuickCheck 、Hypothesis 是不是都能实现这个?
    cloudzhou
        21
    cloudzhou  
       49 天前
    @zpvip 别名就算了,那肯定不是解决之道,清晰是第一位

    我在想,是否有个语言,有 ror 的开发舒适度,同时具备静态编译过程(可选),然后还能编译成为一个独立 bin (类似 Go )
    也就是你在开发阶段,ror 现有开发方式;又是严格类型和语法,ide 完全可解析,比如变量名方法名错误及时提示(加个运行参数)

    那就完美了
    xgdgsc
        22
    xgdgsc  
       49 天前 via Android
    @cloudzhou https://github.com/erikedin/Behavior.jl?tab=readme-ov-file 可以试试 Julia ,就是目前编译独立 bin 在实现进程中,编译出比较大的一坨 dll 目前就可以,1.12rc 在测试编译出小体积的 dll 了。
    doraemon0711
        23
    doraemon0711  
       49 天前   2
    我是力挺 rspec 的一派,因为我认为 rspec 完全发挥了 ruby 的高度定制 DSL 的能力,让写测试就像写文章一样顺畅
    但我也充分理解一个对 ruby 不熟悉的人,或者母语非英语的人看到这些代码会一脸懵的情况,因为我刚开始用 rspec 是就是这样的

    说回 ruby ,我认为其最显著的特点,是用**最简洁**的方式把面向对象发挥到了极致,当意识到 ruby 中任何定义的 class 都是 Class 的实例、`1+1`实际上是`1.+(1)`时,我才意识到什么叫完全面向对象的编程语言,你或许可以指责 ruby 性能不行,但了解过 ruby 的人一定不应该指责其语法不优雅
    flyqie
        24
    flyqie  
       49 天前 via Android
    说起来,有点好奇,ruby 很多设计确实不错,但现在用的似乎并不算多,为什么呢?
    mizuhashi
        25
    mizuhashi  
    OP
       49 天前
    @flyqie 我得者是有的,像 react 的非常落伍但是用的人很多。人的角度我只知道自己很喜 ruby ,14 年的候我要在 ruby python node 面一,然後看了作者 matz 的,很欣人,就定使用 ruby 了,後也遇到了很多用 ruby 的有趣的人(在 qq 群的老是一位 rpgmaker 本的)。我得技背後的人/人文是很重要的,如果要用的人少找原因,大概就是有似想法的人不是多
    mizuhashi
        26
    mizuhashi  
    OP
       49 天前
    @razertory 是的,在 ruby 的方式是,定一代,如 { a },我可以自由定 a 的解方式。上 a 代表的 self.a ,然後 ruby 可以在行每代的候指定 self 是什,再找到的 a 方法,只要具似的特性就能差不多的西
    cloudzhou
        27
    cloudzhou  
       48 天前
    @flyqie 工业化和艺术品的区别,我是个艺术家,要把这个碗做的美的不可方物,耐摔实用一点不在乎;我要生产日用碗,耐摔可靠,最好一天批量生产 1 万个
    kneo
        28
    kneo  
       48 天前 via Android
    有没有可能,别人没有,是因为嫌弃?
    AV1
        29
    AV1  
       48 天前
    @mizuhashi
    a 自动绑定到 self.a ,JS 曾经有类似的语法,也就是 with 块。
    动态绑定 self ,在 JS 里也有类似的 call/apply/bind 。
    还有 eval 、new Function ,可以玩各种很骚的操作。
    但这些特性在 JS 里,通常不被视为优秀,而是糟粕,遭到唾弃。
    或许这就是从“精致的小众玩意”转变到“流水线的大众工具”的代价吧
    qiumaoyuan
        30
    qiumaoyuan  
       48 天前
    @flyqie 语言优秀不优秀,跟用的人多不多没有太大关系。我觉得以逻辑严谨著称的群体,应该要看得清楚这点。

    很多时候反而设计得很糟糕的东西更流行,每当有人谈到排行榜、流行程度,我就总是联想到 MooTools 和 jQuery 。MooTools 设计得十分优秀,无论是源码(喏,你真要看每个函数/方法 10 行左右的代码,MooTools 相当多时候都是),还是暴露出来的接口,都简洁得不像话,但受众就是少得可怜。

    而 jQuery 满世界闻名,流行程度我就不用多说了。但如果你想要复用代码,它就一个插件机制可选,写出来的代码总是又臭又长。反观 MooTools 本身基于对象的设计,OOP 当中那么多的复用手段,它都完全可以利用。

    jQuery 流行的原因是啥?我觉得就是看起来上手简单。但工具这东西,上手难度和往深了用之后的趁手程度往往是两回事。

    另外,如果看排行榜的话,以前 C 和 C++ 一直是老大,这两年 AI 火了,Python 一下窜到第一,所以 Python 突然变成最优秀的语言了吗?明显不是这个原因,对吧?
    table cellpadding="0" cellspacing="0" border="0" width="100%"> qiumaoyuan
        31
    qiumaoyuan  
       48 天前
    说话 RSpec 这东西,我特别喜欢的是它的 context (以及它的各种别名:describe ,example_group 等),这玩意可以把测试场景划分得很细,可以无限层级地复用上下文,简直就是为测试时遇到的各种场景细微区别时,最大限度复用代码而生的。

    当然 TestUnit 和 MiniTest 本身利用好 OOP 也可以做到一样的效果,就是要求使用者熟练掌握 OOP 各项特性。
    mizuhashi
        32
    mizuhashi  
    OP
       48 天前 via iPhone
    @DOLLOR 有道理,用 with this 加 bind 可以似的
    mizuhashi
        33
    mizuhashi  
    OP
       48 天前 via iPhone   1
    @qiumaoyuan rspec 起很快,跑起也很快,免的快
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1162 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 23:36 PVG 07:36 LAX 16:36 JFK 19:36
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86