---
# System prepended metadata

title: RSpec運作模式

---

# RSpec運作模式

### gem “rspec”
![](https://i.imgur.com/CRKhZY9.png)

### gem “rspec-rails”
![](https://i.imgur.com/x6WuwfX.png)

## 運作模式
```ruby=
RSpec.describe "calculation" do
  it "can add numbers" do
    expect(1 + 1).to eq(2)
  end
end
```

### describe
```ruby=
RSpec.describe "calculation" do
 ...
end
```
describe主要源自於example_group.rb
![](https://i.imgur.com/7P6j7Fe.png)

### it
```ruby=
  it "can add numbers" do
    ...
  end
```
it主要源自於example.rb
```ruby=
RSpec.describe "eg1" do
  it "e1"
  it "e2"
  context "eg1_1" do
   it "e3" 
  end
  context "eg2_1" do
    it "e3"
  end
end
```
![](https://i.imgur.com/MpgIZjd.png)


### hook
```ruby=
before {}
after {}
around { |ex| ex.run}
```
hooks源自於hooks.rb
```ruby=
before(:suite) / after(:suite) #只會運行一次
before(:context) / after(:context) #每個example group會運行一次
before / after /arounf #每個example都會運行一次
```

### expect & to
```ruby=
expect(1 + 1).to eq(2)
```
Expectation來自rspec-expectations

![](https://i.imgur.com/ZQCn78E.png)

![](https://i.imgur.com/p2JVXRH.png)


### match
```ruby=
expect(...).to match([
 a_kind_of(String),
 a_hash_including(a: 4)
])
```
```ruby=
[
  “any string”,
  {a: 4, b: 6.0},
]
```
```ruby=
class Match < BaseMatcher
  def match(expected, actual)
    return true if values_match?(
      expected, actual)
    actual.match(expected)
  end
end
```

values_match?
```ruby=
def values_match?(expected, actual)
  expected =
    with_matchers_cloned(expected)
  Support::FuzzyMatcher.values_match?(
    expected, actual)
end
```
Support::FuzzyMatcher.values_match?
```ruby=
def self.values_match?(e, a)
  if Hash === a
    return hashes_match?(e, a) if Hash
                                  === e
  elsif Array === e
   && Enumerable === a
   && !(Struct === a)
  return arrays_match?(
    e, a)
end
```

hashes_match?
```ruby=
def self.hashes_match?(
    expected_hash, actual_hash)
  return false if
    expected_hash.size != actual_hash.size
  expected_hash.all? do |e_k, e_v|
    actual_value = actual_hash.fetch(e_k) {
      return false }
    values_match?(e_v, actual_value)
end end
```

#### Array === e
arrays_match?
```ruby=
def self.arrays_match?(
  expected_list, actual_list)
  return false if
    expected_list.size!=actual_list.size
  expected_list.zip(actual_list).all? do
    |expected, actual|
    values_match?(expected, actual)
  end
end
```

### 值與屬性
```ruby=
def self.values_match?(expected, actual) 
    ... 
    (the code we saw earlier) 
    return true if expected == actual 
  begin
    expected === actual
  rescue ArgumentError
    # Some objects, like 0-arg lambdas on
    # 1.9+, raise
    # ArgumentError for `expected === actual`.
    false
  end 
end
```

原比對內容
```ruby=
[
  “any string”,
  {a: 4, b: 6.0}
]
```
做了三件比對：arrays_match? / === / ===

### rspec-mocks
```ruby=
RSpec.describe "mocking" do
  it “adds a foo method" do
    allow(foo).to
      receive(:bees)
    expect(foo.bees).to eq(nil)
  end
end
```


![](https://i.imgur.com/OWXXRzx.png)

![](https://i.imgur.com/TBhp6E3.png)

call foo.bee
![](https://i.imgur.com/ic0g5Kn.png)

## RSpec運作流程
![](https://i.imgur.com/UpZP3Gj.png)
