RSpec 入门教程
FreeGuideOnline
最新
2026-07-14
bash ruby -v
### 2. 初始化项目
在你的项目目录中,如果还没有 Gemfile,先创建:
```bash
bundle init
3. 添加 RSpec
编辑 Gemfile,加入 RSpec:
gem 'rspec', '~> 3.12'
然后执行安装:
bundle install
4. 初始化 RSpec 配置
bundle exec rspec --init
这会生成 .rspec 和 spec/spec_helper.rb 文件。
第一个 RSpec 测试
假设你有一个计算器类,我们为它的 add 方法写测试。
创建待测试类
在 lib/calculator.rb 中:
class Calculator
def add(a, b)
a + b
end
end
编写测试规格
在 spec/calculator_spec.rb 中:
require 'calculator'
RSpec.describe Calculator do
describe '#add' do
it 'returns the sum of two numbers' do
calc = Calculator.new
expect(calc.add(2, 3)).to eq(5)
end
end
end
运行测试
bundle exec rspec
看到绿色输出表示测试通过。
RSpec 核心结构
describe 与 context
describe用于组织一组相关测试,通常用来描述类名或方法名。context是describe的别名,用于区分不同场景(当输入不同时)。
describe Calculator do
describe '#divide' do
context 'when divisor is not zero' do
it 'returns the division result' # ...
end
context 'when divisor is zero' do
it 'raises an error' # ...
end
end
end
it 与 example
it 定义一个具体的测试用例(也叫 example)。其后的字符串描述预期行为。
期望(Expectations)
expect(实际值).to 匹配器(期望值) 是 RSpec 的核心断言语法。
常用匹配器:
eq:严格相等(比==更严格)be:用于比较同一对象 / 布尔值be_truthy/be_falseyinclude:检查集合是否包含某个元素match:正则匹配raise_error:检查是否抛出异常
expect(10).to eq(10)
expect(true).to be true
expect([1,2,3]).to include(2)
expect { 1 / 0 }.to raise_error(ZeroDivisionError)
测试辅助机制
before 与 after 钩子
before(:each) 在每个测试前运行,常用于初始化状态。
describe Game do
before(:each) do
@game = Game.new
@game.start
end
it 'has initial score zero' do
expect(@game.score).to eq(0)
end
end
before(:all) 在整个 describe 块前运行一次(谨慎使用,避免状态共享)。
let 与 let!
let 用于干净地定义延迟求值的变量,适合替代实例变量。
describe User do
let(:user) { User.new(age: 25) }
it 'is not underage' do
expect(user).not_to be_underage
end
end
let所定义的变量,在第一次访问时才会创建,且在同一 example 内会缓存。let!则会在每个 example 运行前立即创建(类似before但更精确)。
subject
subject 用于定义测试主体,使测试更简洁。
describe Array do
subject { [1, 2, 3] }
it 'has length 3' do
expect(subject.length).to eq(3)
end
# 也可以使用懒人写法 it { is_expected.to ... }
it { is_expected.to include(2) }
end
测试替身 (Mocks & Stubs)
RSpec 内置了双替身(doubles)功能,用于隔离被测对象的依赖。
方法桩 (Stubs)
使用 allow 固定某个对象方法的返回值。
weather_api = double('WeatherAPI')
allow(weather_api).to receive(:temperature).and_return(22)
report = WeatherReport.new(weather_api)
expect(report.describe).to eq('Warm')
消息期望 (Mock)
使用 expect 来验证对象是否收到了某个消息。
expect(weather_api).to receive(:temperature).and_return(22)
上述代码在测试结束时,如果没有调用 temperature,测试就会失败。
组织测试文件与标签
目录结构
推荐遵循约定:
project/
├── lib/
│ └── calculator.rb
└── spec/
├── calculator_spec.rb
└── spec_helper.rb
使用标签过滤测试
可以在 describe 或 it 上添加标签,运行时过滤。
it 'runs a slow process', :slow do
# ...
end
运行特定标签测试:
bundle exec rspec --tag slow
排除标签订:
bundle exec rspec --tag ~slow
测试驱动开发(TDD)实践
- 写一个失败的测试:描述你希望看到的行
- 编写最少代码让测试通过:不要过度设计
- 重构:优化代码结构,确保测试仍然通过
RSpec 的报告格式能清晰展示失败信息,帮你快速定位问题。使用 --format documentation 可以看到详细描述。
常用配置与技巧
总是在 spec_helper 中 require 项目文件
可以在 spec_helper.rb 顶部引入 lib 路径,避免每次手动 require。
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
require 'calculator'
开启 --warnings 与 --order random
.rspec 文件里可以添加:
--format documentation
--color
--order random