|
1 |
| -# rspec-core [](http://travis-ci.org/rspec/rspec-core) [](https://codeclimate.com/github/rspec/rspec-core) |
| 1 | +# rspec-core |
2 | 2 |
|
3 |
| -rspec-core provides the structure for writing executable examples of how your |
4 |
| -code should behave, and an `rspec` command with tools to constrain which |
5 |
| -examples get run and tailor the output. |
6 |
| - |
7 |
| -## Install |
8 |
| - |
9 |
| - gem install rspec # for rspec-core, rspec-expectations, rspec-mocks |
10 |
| - gem install rspec-core # for rspec-core only |
11 |
| - rspec --help |
12 |
| - |
13 |
| -Want to run against the `master` branch? You'll need to include the dependent |
14 |
| -RSpec repos as well. Add the following to your `Gemfile`: |
15 |
| - |
16 |
| -```ruby |
17 |
| -%w[rspec rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib| |
18 |
| - gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'master' |
19 |
| -end |
20 |
| -``` |
21 |
| - |
22 |
| -## Basic Structure |
23 |
| - |
24 |
| -RSpec uses the words "describe" and "it" so we can express concepts like a conversation: |
25 |
| - |
26 |
| - "Describe an order." |
27 |
| - "It sums the prices of its line items." |
28 |
| - |
29 |
| -```ruby |
30 |
| -RSpec.describe Order do |
31 |
| - it "sums the prices of its line items" do |
32 |
| - order = Order.new |
33 |
| - |
34 |
| - order.add_entry(LineItem.new(:item => Item.new( |
35 |
| - :price => Money.new(1.11, :USD) |
36 |
| - ))) |
37 |
| - order.add_entry(LineItem.new(:item => Item.new( |
38 |
| - :price => Money.new(2.22, :USD), |
39 |
| - :quantity => 2 |
40 |
| - ))) |
41 |
| - |
42 |
| - expect(order.total).to eq(Money.new(5.55, :USD)) |
43 |
| - end |
44 |
| -end |
45 |
| -``` |
46 |
| - |
47 |
| -The `describe` method creates an [ExampleGroup](http://rubydoc.info/gems/rspec-core/RSpec/Core/ExampleGroup). Within the |
48 |
| -block passed to `describe` you can declare examples using the `it` method. |
49 |
| - |
50 |
| -Under the hood, an example group is a class in which the block passed to |
51 |
| -`describe` is evaluated. The blocks passed to `it` are evaluated in the |
52 |
| -context of an _instance_ of that class. |
53 |
| - |
54 |
| -## Nested Groups |
55 |
| - |
56 |
| -You can also declare nested groups using the `describe` or `context` |
57 |
| -methods: |
58 |
| - |
59 |
| -```ruby |
60 |
| -RSpec.describe Order do |
61 |
| - context "with no items" do |
62 |
| - it "behaves one way" do |
63 |
| - # ... |
64 |
| - end |
65 |
| - end |
66 |
| - |
67 |
| - context "with one item" do |
68 |
| - it "behaves another way" do |
69 |
| - # ... |
70 |
| - end |
71 |
| - end |
72 |
| -end |
73 |
| -``` |
74 |
| - |
75 |
| -Nested groups are subclasses of the outer example group class, providing |
76 |
| -the inheritance semantics you'd want for free. |
77 |
| - |
78 |
| -## Aliases |
79 |
| - |
80 |
| -You can declare example groups using either `describe` or `context`. |
81 |
| -For a top level example group, `describe` and `context` are available |
82 |
| -off of `RSpec`. For backwards compatibility, they are also available |
83 |
| -off of the `main` object and `Module` unless you disable monkey |
84 |
| -patching. |
85 |
| - |
86 |
| -You can declare examples within a group using any of `it`, `specify`, or |
87 |
| -`example`. |
88 |
| - |
89 |
| -## Shared Examples and Contexts |
90 |
| - |
91 |
| -Declare a shared example group using `shared_examples`, and then include it |
92 |
| -in any group using `include_examples`. |
93 |
| - |
94 |
| -```ruby |
95 |
| -RSpec.shared_examples "collections" do |collection_class| |
96 |
| - it "is empty when first created" do |
97 |
| - expect(collection_class.new).to be_empty |
98 |
| - end |
99 |
| -end |
100 |
| - |
101 |
| -RSpec.describe Array do |
102 |
| - include_examples "collections", Array |
103 |
| -end |
104 |
| - |
105 |
| -RSpec.describe Hash do |
106 |
| - include_examples "collections", Hash |
107 |
| -end |
108 |
| -``` |
109 |
| - |
110 |
| -Nearly anything that can be declared within an example group can be declared |
111 |
| -within a shared example group. This includes `before`, `after`, and `around` |
112 |
| -hooks, `let` declarations, and nested groups/contexts. |
113 |
| - |
114 |
| -You can also use the names `shared_context` and `include_context`. These are |
115 |
| -pretty much the same as `shared_examples` and `include_examples`, providing |
116 |
| -more accurate naming when you share hooks, `let` declarations, helper methods, |
117 |
| -etc, but no examples. |
118 |
| - |
119 |
| -## Metadata |
120 |
| - |
121 |
| -rspec-core stores a metadata hash with every example and group, which |
122 |
| -contains their descriptions, the locations at which they were |
123 |
| -declared, etc, etc. This hash powers many of rspec-core's features, |
124 |
| -including output formatters (which access descriptions and locations), |
125 |
| -and filtering before and after hooks. |
126 |
| - |
127 |
| -Although you probably won't ever need this unless you are writing an |
128 |
| -extension, you can access it from an example like this: |
129 |
| - |
130 |
| -```ruby |
131 |
| -it "does something" do |example| |
132 |
| - expect(example.metadata[:description]).to eq("does something") |
133 |
| -end |
134 |
| -``` |
135 |
| - |
136 |
| -### `described_class` |
137 |
| - |
138 |
| -When a class is passed to `describe`, you can access it from an example |
139 |
| -using the `described_class` method, which is a wrapper for |
140 |
| -`example.metadata[:described_class]`. |
141 |
| - |
142 |
| -```ruby |
143 |
| -RSpec.describe Widget do |
144 |
| - example do |
145 |
| - expect(described_class).to equal(Widget) |
146 |
| - end |
147 |
| -end |
148 |
| -``` |
149 |
| - |
150 |
| -This is useful in extensions or shared example groups in which the specific |
151 |
| -class is unknown. Taking the collections shared example group from above, we can |
152 |
| -clean it up a bit using `described_class`: |
153 |
| - |
154 |
| -```ruby |
155 |
| -RSpec.shared_examples "collections" do |
156 |
| - it "is empty when first created" do |
157 |
| - expect(described_class.new).to be_empty |
158 |
| - end |
159 |
| -end |
160 |
| - |
161 |
| -RSpec.describe Array do |
162 |
| - include_examples "collections" |
163 |
| -end |
164 |
| - |
165 |
| -RSpec.describe Hash do |
166 |
| - include_examples "collections" |
167 |
| -end |
168 |
| -``` |
169 |
| - |
170 |
| -## A Word on Scope |
171 |
| - |
172 |
| -RSpec has two scopes: |
173 |
| - |
174 |
| -* **Example Group**: Example groups are defined by a `describe` or |
175 |
| - `context` block, which is eagerly evaluated when the spec file is |
176 |
| - loaded. The block is evaluated in the context of a subclass of |
177 |
| - `RSpec::Core::ExampleGroup`, or a subclass of the parent example group |
178 |
| - when you're nesting them. |
179 |
| -* **Example**: Examples -- typically defined by an `it` block -- and any other |
180 |
| - blocks with per-example semantics -- such as a `before(:example)` hook -- are |
181 |
| - evaluated in the context of |
182 |
| - an _instance_ of the example group class to which the example belongs. |
183 |
| - Examples are _not_ executed when the spec file is loaded; instead, |
184 |
| - RSpec waits to run any examples until all spec files have been loaded, |
185 |
| - at which point it can apply filtering, randomization, etc. |
186 |
| - |
187 |
| -To make this more concrete, consider this code snippet: |
188 |
| - |
189 |
| -``` ruby |
190 |
| -RSpec.describe "Using an array as a stack" do |
191 |
| - def build_stack |
192 |
| - [] |
193 |
| - end |
194 |
| - |
195 |
| - before(:example) do |
196 |
| - @stack = build_stack |
197 |
| - end |
198 |
| - |
199 |
| - it 'is initially empty' do |
200 |
| - expect(@stack).to be_empty |
201 |
| - end |
202 |
| - |
203 |
| - context "after an item has been pushed" do |
204 |
| - before(:example) do |
205 |
| - @stack.push :item |
206 |
| - end |
207 |
| - |
208 |
| - it 'allows the pushed item to be popped' do |
209 |
| - expect(@stack.pop).to eq(:item) |
210 |
| - end |
211 |
| - end |
212 |
| -end |
213 |
| -``` |
214 |
| - |
215 |
| -Under the covers, this is (roughly) equivalent to: |
216 |
| - |
217 |
| -``` ruby |
218 |
| -class UsingAnArrayAsAStack < RSpec::Core::ExampleGroup |
219 |
| - def build_stack |
220 |
| - [] |
221 |
| - end |
222 |
| - |
223 |
| - def before_example_1 |
224 |
| - @stack = build_stack |
225 |
| - end |
226 |
| - |
227 |
| - def it_is_initially_empty |
228 |
| - expect(@stack).to be_empty |
229 |
| - end |
230 |
| - |
231 |
| - class AfterAnItemHasBeenPushed < self |
232 |
| - def before_example_2 |
233 |
| - @stack.push :item |
234 |
| - end |
235 |
| - |
236 |
| - def it_allows_the_pushed_item_to_be_popped |
237 |
| - expect(@stack.pop).to eq(:item) |
238 |
| - end |
239 |
| - end |
240 |
| -end |
241 |
| -``` |
242 |
| - |
243 |
| -To run these examples, RSpec would (roughly) do the following: |
244 |
| - |
245 |
| -``` ruby |
246 |
| -example_1 = UsingAnArrayAsAStack.new |
247 |
| -example_1.before_example_1 |
248 |
| -example_1.it_is_initially_empty |
249 |
| - |
250 |
| -example_2 = UsingAnArrayAsAStack::AfterAnItemHasBeenPushed.new |
251 |
| -example_2.before_example_1 |
252 |
| -example_2.before_example_2 |
253 |
| -example_2.it_allows_the_pushed_item_to_be_popped |
254 |
| -``` |
255 |
| - |
256 |
| -## The `rspec` Command |
257 |
| - |
258 |
| -When you install the rspec-core gem, it installs the `rspec` executable, |
259 |
| -which you'll use to run rspec. The `rspec` command comes with many useful |
260 |
| -options. |
261 |
| -Run `rspec --help` to see the complete list. |
262 |
| - |
263 |
| -## Store Command Line Options `.rspec` |
264 |
| - |
265 |
| -You can store command line options in a `.rspec` file in the project's root |
266 |
| -directory, and the `rspec` command will read them as though you typed them on |
267 |
| -the command line. |
268 |
| - |
269 |
| -## Get Started |
270 |
| - |
271 |
| -Start with a simple example of behavior you expect from your system. Do |
272 |
| -this before you write any implementation code: |
273 |
| - |
274 |
| -```ruby |
275 |
| -# in spec/calculator_spec.rb |
276 |
| -RSpec.describe Calculator do |
277 |
| - describe '#add' do |
278 |
| - it 'returns the sum of its arguments' do |
279 |
| - expect(Calculator.new.add(1, 2)).to eq(3) |
280 |
| - end |
281 |
| - end |
282 |
| -end |
283 |
| -``` |
284 |
| - |
285 |
| -Run this with the rspec command, and watch it fail: |
286 |
| - |
287 |
| -``` |
288 |
| -$ rspec spec/calculator_spec.rb |
289 |
| -./spec/calculator_spec.rb:1: uninitialized constant Calculator |
290 |
| -``` |
291 |
| - |
292 |
| -Address the failure by defining a skeleton of the `Calculator` class: |
293 |
| - |
294 |
| -```ruby |
295 |
| -# in lib/calculator.rb |
296 |
| -class Calculator |
297 |
| - def add(a, b) |
298 |
| - end |
299 |
| -end |
300 |
| -``` |
301 |
| - |
302 |
| -Be sure to require the implementation file in the spec: |
303 |
| - |
304 |
| -```ruby |
305 |
| -# in spec/calculator_spec.rb |
306 |
| -# - RSpec adds ./lib to the $LOAD_PATH |
307 |
| -require "calculator" |
308 |
| -``` |
309 |
| - |
310 |
| -Now run the spec again, and watch the expectation fail: |
311 |
| - |
312 |
| -``` |
313 |
| -$ rspec spec/calculator_spec.rb |
314 |
| -F |
315 |
| -
|
316 |
| -Failures: |
317 |
| -
|
318 |
| - 1) Calculator#add returns the sum of its arguments |
319 |
| - Failure/Error: expect(Calculator.new.add(1, 2)).to eq(3) |
320 |
| -
|
321 |
| - expected: 3 |
322 |
| - got: nil |
323 |
| -
|
324 |
| - (compared using ==) |
325 |
| - # ./spec/calcalator_spec.rb:6:in `block (3 levels) in <top (required)>' |
326 |
| -
|
327 |
| -Finished in 0.00131 seconds (files took 0.10968 seconds to load) |
328 |
| -1 example, 1 failure |
329 |
| -
|
330 |
| -Failed examples: |
331 |
| -
|
332 |
| -rspec ./spec/calcalator_spec.rb:5 # Calculator#add returns the sum of its arguments |
333 |
| -``` |
334 |
| - |
335 |
| -Implement the simplest solution, by changing the definition of `Calculator#add` to: |
336 |
| - |
337 |
| -```ruby |
338 |
| -def add(a, b) |
339 |
| - a + b |
340 |
| -end |
341 |
| -``` |
342 |
| - |
343 |
| -Now run the spec again, and watch it pass: |
344 |
| - |
345 |
| -``` |
346 |
| -$ rspec spec/calculator_spec.rb |
347 |
| -. |
348 |
| -
|
349 |
| -Finished in 0.000315 seconds |
350 |
| -1 example, 0 failures |
351 |
| -``` |
352 |
| - |
353 |
| -Use the `documentation` formatter to see the resulting spec: |
354 |
| - |
355 |
| -``` |
356 |
| -$ rspec spec/calculator_spec.rb --format doc |
357 |
| -Calculator |
358 |
| - #add |
359 |
| - returns the sum of its arguments |
360 |
| -
|
361 |
| -Finished in 0.000379 seconds |
362 |
| -1 example, 0 failures |
363 |
| -``` |
364 |
| - |
365 |
| -## Contributing |
366 |
| - |
367 |
| -Once you've set up the environment, you'll need to cd into the working |
368 |
| -directory of whichever repo you want to work in. From there you can run the |
369 |
| -specs and cucumber features, and make patches. |
370 |
| - |
371 |
| -NOTE: You do not need to use rspec-dev to work on a specific RSpec repo. You |
372 |
| -can treat each RSpec repo as an independent project. |
373 |
| - |
374 |
| -* [Build details](BUILD_DETAIL.md) |
375 |
| -* [Code of Conduct](CODE_OF_CONDUCT.md) |
376 |
| -* [Detailed contributing guide](CONTRIBUTING.md) |
377 |
| -* [Development setup guide](DEVELOPMENT.md) |
378 |
| - |
379 |
| -## Also see |
380 |
| - |
381 |
| -* [https://github.com/rspec/rspec](https://github.com/rspec/rspec) |
382 |
| -* [https://github.com/rspec/rspec-expectations](https://github.com/rspec/rspec-expectations) |
383 |
| -* [https://github.com/rspec/rspec-mocks](https://github.com/rspec/rspec-mocks) |
384 |
| -* [https://github.com/rspec/rspec-rails](https://github.com/rspec/rspec-rails) |
| 3 | +This branch is deprecated, please use main`. |
0 commit comments