File: Transactions.md

package info (click to toggle)
ruby-rspec-rails 8.0.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,804 kB
  • sloc: ruby: 10,881; sh: 198; makefile: 6
file content (99 lines) | stat: -rw-r--r-- 2,904 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# Transactions

When you run `rails generate rspec:install`, the `spec/rails_helper.rb` file
includes the following configuration:

```ruby
RSpec.configure do |config|
  config.use_transactional_fixtures = true
end
```

The name of this setting is a bit misleading. What it really means in Rails
is "run every test method within a transaction." In the context of rspec-rails,
it means "run every example within a transaction."

The idea is to start each example with a clean database, create whatever data
is necessary for that example, and then remove that data by simply rolling back
the transaction at the end of the example.

For how this affects methods exposing transaction visibility see:
https://guides.rubyonrails.org/testing.html#transactions

### Disabling transactions

If you prefer to manage the data yourself, or using another tool like
[database_cleaner](https://github.com/bmabey/database_cleaner) to do it for you,
simply tell RSpec to tell Rails not to manage transactions:

```ruby
RSpec.configure do |config|
  config.use_transactional_fixtures = false
end
```

### Data created in `before(:example)` are rolled back

Any data you create in a `before(:example)` hook will be rolled back at the end of
the example. This is a good thing because it means that each example is
isolated from state that would otherwise be left around by the examples that
already ran. For example:

```ruby
describe Widget do
  before(:example) do
    @widget = Widget.create
  end

  it "does something" do
    expect(@widget).to do_something
  end

  it "does something else" do
    expect(@widget).to do_something_else
  end
end
```

The `@widget` is recreated in each of the two examples above, so each example
has a different object, _and_ the underlying data is rolled back so the data
backing the `@widget` in each example is new.

### Data created in `before(:context)` are _not_ rolled back

`before(:context)` hooks are invoked before the transaction is opened. You can use
this to speed things up by creating data once before any example in a group is
run, however, this introduces a number of complications and you should only do
this if you have a firm grasp of the implications. Here are a couple of
guidelines:

1.  Be sure to clean up any data in an `after(:context)` hook:

    ```ruby
    before(:context) do
      @widget = Widget.create!
    end

    after(:context) do
      @widget.destroy
    end
    ```

    If you don't do that, you'll leave data lying around that will eventually
interfere with other examples.

2.  Reload the object in a `before(:example)` hook.

    ```ruby
    before(:context) do
      @widget = Widget.create!
    end

    before(:example) do
      @widget.reload
    end
    ```

Even though database updates in each example will be rolled back, the
object won't _know_ about those rollbacks so the object and its backing
data can easily get out of sync.