为什么需要写单元ref="/tag/192/" style="color:#B2A89E;font-weight:bold;">测试
在开发一个后台管理系统时,你修改了一段用户权限判断的代码,本以为只是小调整,结果上线后发现登录功能出问题了。这种情况其实很常见,而单元测试就是帮你提前发现问题的“安全网”。Ruby 作为一门广泛用于 Web 开发的语言,尤其在使用 Rails 框架时,写单元测试几乎是标配。
使用 Minitest 快速上手
Ruby 自带 Minitest,不需要额外安装就能写测试。假设你有一个计算商品折扣的方法:
class Product
def self.discount_price(original, rate)
original * (1 - rate)
end
end你可以为它写一个简单的测试来验证逻辑是否正确。
require 'minitest/autorun'
class TestProduct < Minitest::Test
def test_discount_price
result = Product.discount_price(100, 0.2)
assert_equal 80, result
end
end运行这个文件,如果输出中显示 . 并提示测试通过,说明你的方法工作正常。
理解 assert 的基本用法
测试的核心是断言(assert)。比如 assert_equal 判断两个值是否相等,assert_nil 检查是否为空,assert_raises 可以验证某个操作是否会抛出异常。
例如,你想确保折扣率超过 1 时会报错:
def test_invalid_rate_raises_error
assert_raises(ArgumentError) do
Product.discount_price(100, 1.5)
end
end使用 RSpec 让测试更清晰
虽然 Minitest 足够简单,但很多人更喜欢 RSpec 的语法,读起来像自然语言。先在 Gemfile 中添加:
gem 'rspec'然后运行 bundle install,再执行 rspec --init 初始化项目。
接着创建测试文件 product_spec.rb:
require 'rspec'
require_relative 'product'
RSpec.describe Product do
describe '.discount_price' do
it 'returns discounted price' do
expect(Product.discount_price(100, 0.1)).to eq(90)
end
it 'raises error for invalid rate' do
expect {
Product.discount_price(100, 2.0)
}.to raise_error(ArgumentError)
end
end
end这种写法更贴近“描述行为”的方式,团队协作时更容易理解。
测试真实场景:订单金额计算
假设你在做一个电商系统,订单总金额要根据商品数量、单价和会员折扣计算。可以先写出方法:
class OrderCalculator
def self.total(items, is_vip: false)
base = items.sum { |price, qty| price * qty }
is_vip ? base * 0.9 : base
end
end对应的测试可以这样写:
RSpec.describe OrderCalculator do
describe '.total' do
let(:items) { [[100, 2], [50, 1]] } # 两件100元,一件50元
it 'calculates normal total' do
expect(OrderCalculator.total(items)).to eq(250)
end
it 'applies vip discount' do
expect(OrderCalculator.total(items, is_vip: true)).to eq(225)
end
end
end当业务逻辑变复杂时,这些测试能确保每次改动都不会意外破坏原有功能。
让测试成为日常习惯
刚开始写测试可能会觉得多此一举,尤其是功能看起来已经跑通了。但就像每天上班打卡一样,写测试是一种保障。改代码前先看测试,加功能时顺手补个测试,久而久之就会发现 bug 出现的频率明显下降,重构也更有底气。