That’s a few questions. Let me see if I can provide a little perspective on them. Note these are just my opinions and do not reflect any RSpec standard / best practice / recommendation / etc.
…is not wise to use
before
This is largely a matter of opinion. Now, you may have come across something stating that before(:all) or it’s new more intent revealing alias before(:context) is not wise to use. In general, I agree with this view. This is only run once before the entire example group (context) it is defined in. There are rare cases where this is what you need, but generally, it’s not what you need. It usually leaks mutable state across specs, which can have very surprising and difficult to debug consequences. It also has many caveats regarding DB state, transactions, and other system level dependencies.
On the other hand, before(:each) or the new before(:example), which is the default if you just write before, it more acceptable to use. It is also normally what you want. It keeps state clean across specs, it hooks into transactions, rspec-mocks, etc. Unless you have a provable, with valid data showing it, case where this is detrimental to performance, it’s the proper choice.
Personally, I feel before and let should be used sparingly. They should be reserved for cases when you mean to state: “All of the following specs in this group really must have this setup action done before each of them.”
Note I wrote “setup action”. This does not apply to any action regarding the object under test. I personally believe those should not be put in any before block, but explicitly listed in the spec so that it is clear what you are testing, versus what is setting up the testing environment. Let me share a code sample explaining this view:
RSpec.describe Array, "list of elements" do
describe "popping the array" do
let(:element_list) do
[:first, :second, :last]
end
# This is the same as before(:each) or before(:example)
before do
element_list.pop
end
# Note we are **cannot** use the same setup state since we need to capture
# the value
it "returns the last value of the array" do
expect([:first, :second, :last].pop).to eq(:last)
end
# Note it's very unclear what the test is doing just by looking at it. We
# are describing an action on an Array instance. Yet, that isn't clear from
# this spec at all, since all we see is that we check the array elements
it "mutates the array removing the last element" do
expect(element_list).to eq([:first, :second])
end
end
end
I’ve seen the above code “cleaned up” to be more expressive by converting it into something like:
RSpec.describe Array, "list of elements" do
describe "popping the array" do
let(:array_with_elements) do
[:first, :second, :last]
end
# This is idiom is often viewed as an implicit `before`.
#
# Except, it's not. It's only run once you send the `element_list` message.
# If you do this in the wrong spot then it may lead to surprising tests.
#
# This often leads people to use bang form `subject!`, which is
# functionally the same as:
#
# subject(:element_list) { ... }
# before { element_list }
#
# But this is often not the right tool and can have some surprising issues:
# http://aaronkromer.com/blog/2013-05-19-the-bang-is-for-surprise.html
#
# Additionally, using `subject` here, IMO, is just wrong. The subject is
# not the element returned from `#pop`. It's a specific instance of
# `Array`. We are testing the behavior of `#pop`.
subject(:popped_element) do
array_with_elements.pop
end
# Looks clean, but is misleading as to what is actually the subject of the
# spec. Here it's the popped element, but in reality, it's the return value
# of calling the action `pop`.
it "returns the last value of the array" do
expect(popped_element).to eq(:last)
end
# This turns out to appear trickier to test. Often the initial pass looks
# something like this:
#
# expect(array_with_elements).to eq([:first, :second])
#
# That will fail though. Why? Because if we are not using the bang form of
# `subject!`, a `before`, or something like `preload` (see blog link
# above) then nothing is actually popped before this spec is run. Thus the
# array is never mutated. Leaving `array_with_elements` unmodified.
it "mutates the array removing the last element" do
popped_element
expect(array_with_elements).to eq([:first, :second])
end
end
end
I’ve also seen people go the following route, as well, it looks a bit cleaner:
# Looks cleaner using ivars. In general, I am not a fan of ivars and instead
# prefer to use local variables and message passing. This allows for clean
# extractions of ideas / concepts without modifying lots of code.
#
# For more details see this SO answer: http://stackoverflow.com/a/5359979/47438
RSpec.describe Array, "list of elements" do
describe "popping the array" do
before do
@element_list = [:first, :second, :last]
@popped_element = @element_list.pop
end
# Looks clean, but is misleading as to what is actually the subject of the
# spec. Here it's the popped element, but in reality, it's the return value
# of calling the action `pop`.
it "returns the last value of the array" do
expect(@popped_element).to eq(:last)
end
# Again, looks clean, but is misleading. Here we are actually looking at
# the real object under test (the proper subject). But, it's already
# mutated. We still have to hunt down that mutation.
it "mutates the array removing the last element" do
expect(@element_list).to eq([:first, :second])
end
end
end
Another large issue I have with the last form above is it completely hides the mutation and behavior being tested in the before block. Just looking at the before block, it’s not obvious what is setup and what is being tested. It isn’t until I’ve read all of the specs, or a large number of them, that things become clear to me.
This is why, I prefer a form more along the lines of:
RSpec.describe Array, "list of elements" do
describe "popping the array" do
subject(:element_list) do
[:first, :second, :last]
end
it "returns the last value of the array" do
expect(element_list.pop).to eq(:last)
end
it "mutates the array removing the last element" do
expect{ element_list.pop }.to change{ element_list }
.from([:first, :second, :last])
.to([:first, :second])
end
end
end
To me this is cleaner, more explicit, and more obvious what is happening. But I’m just one person.
way to test validations
I would test them just as I would any side-effect based protocol:
RSpec.describe Admin, type: :model do
describe "requires a name" do
subject(:nameless_admin) do
Admin.new(name: nil)
end
it "is not valid" do
expect(nameless_admin.valid?).to be false
end
it "sets an error on the name attribute" do
expect{ nameless_admin.valid? }
.to change{ nameless_admin.errors[:name] }
.from([])
.to(include "can't be blank")
end
end
end
inout of a form by using capybara
This is a bit broader. In general, I simply go to the web page, fill in the form, then check the response. These are usually feature type acceptance specs. When I write these, I sometimes don’t adhere to the “one expectation per spec” guideline. This is because I am often testing a workflow, and I need to validate my expected assumptions along the path.
RSpec.describe "Signing up for the site", type: :feature do
it "requires a name to be specified" do
visit sign_up_url
click_button "Sign Up"
expect(current_url).to eq sign_up_url
expect(page).to have_contenxt "Name can't be blank"
end
end
Checkout the capybara readme (https://github.com/jnicklas/capybara) for more examples.
Happy testing!
Aaron
--
You received this message because you are subscribed to the Google Groups "rspec" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rspec+un...@googlegroups.com.
To post to this group, send email to rs...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/rspec/5a32ddf8-f0fc-4bcd-80cd-555118b50ef1%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
describe User do before { @user = User.new(name: "Example User", email: "us...@example.com") }
before_save { self.email = email.downcase }
And from googling I get the impression that this is not the right way so that is why I asked here.
Roelof
...<div title="MDH:VGhhdCdzIGEgZmV3IHF1ZXN0aW9ucy4gTGV0IG1lIHNlZSBpZiBJIGNhbiBwcm92aWRlIGEgbGl0 dGxlIHBlcnNwZWN0aXZlIG9uIHRoZW0uIF9Ob3RlIHRoZXNlIGFyZSBqdXN0IG15IG9waW5pb25z IGFuZCBkbyBub3QgcmVmbGVjdCBhbnkgUlNwZWMgc3RhbmRhcmQgLyBiZXN0IHByYWN0aWNlIC8g cmVjb21tZW5kYXRpb24gLyBldGMuXzxkaXY+PGJyPjwvZGl2PjxkaXY+Jmd0OyAuLi48c3BhbiBz dHlsZT0iZm9udC1mYW1pbHk6IGFyaWFsLCBzYW5zLXNlcmlmOyBmb250LXNpemU6IDEzcHg7Ij5p cyBub3Qgd2lzZSB0byB1c2UgYGJlZm9yZWA8L3NwYW4+PC9kaXY+PGRpdj48c3BhbiBzdHlsZT0i Zm9udC1mYW1pbHk6IGFyaWFsLCBzYW5zLXNlcmlmOyBmb250LXNpemU6IDEzcHg7Ij48YnI+PC9z cGFuPjwvZGl2PjxkaXY+PHNwYW4gc3R5bGU9ImZvbnQtZmFtaWx5OiBhcmlhbCwgc2Fucy1zZXJp ZjsgZm9udC1zaXplOiAxM3B4OyI+VGhpcyBpcyBsYXJnZWx5IGEgbWF0dGVyIG9mIG9waW5pb24u Jm5ic3A7PC9zcGFuPjxzcGFuIHN0eWxlPSJmb250LWZhbWlseTogYXJpYWwsIHNhbnMtc2VyaWY7 IGZvbnQtc2l6ZTogMTNweDsiPk5vdywgeW91IG1heSBoYXZlIGNvbWUgYWNyb3NzIHNvbWV0aGlu ZyBzdGF0aW5nIHRoYXQgYGJlZm9yZSg6YWxsKWAgb3IgaXQncyBuZXcgbW9yZSBpbnRlbnQgcmV2 ZWFsaW5nIGFsaWFzIGBiZWZvcmUoOmNvbnRleHQpYCBpcyBub3Qgd2lzZSB0byB1c2UuIEluIGdl bmVyYWwsIEkgYWdyZWUgd2l0aCB0aGlzIHZpZXcuIFRoaXMgaXMgb25seSBydW4gb25jZSBiZWZv cmUgdGhlIGVudGlyZSBleGFtcGxlIGdyb3VwIChjb250ZXh0KSBpdCBpcyBkZWZpbmVkIGluLiBU aGVyZSBhcmUgcmFyZSBjYXNlcyB3aGVyZSB0aGlzIGlzIHdoYXQgeW91IG5lZWQsIGJ1dCBnZW5l cmFsbHksIGl0J3Mgbm90IHdoYXQgeW91IG5lZWQuIEl0IHVzdWFsbHkgbGVha3MgbXV0YWJsZSBz dGF0ZSBhY3Jvc3Mgc3BlY3MsIHdoaWNoIGNhbiBoYXZlIHZlcnkgc3VycHJpc2luZyBhbmQgZGlm ZmljdWx0IHRvIGRlYnVnIGNvbnNlcXVlbmNlcy4gSXQgYWxzbyBoYXMgbWFueSBjYXZlYXRzIHJl Z2FyZGluZyBEQiBzdGF0ZSwgdHJhbnNhY3Rpb25zLCBhbmQgb3RoZXIgc3lzdGVtIGxldmVsIGRl cGVuZGVuY2llcy48L3NwYW4+PC9kaXY+PGRpdj48c3BhbiBzdHlsZT0iZm9udC1mYW1pbHk6IGFy aWFsLCBzYW5zLXNlcmlmOyBmb250LXNpemU6IDEzcHg7Ij48YnI+PC9zcGFuPjwvZGl2PjxkaXY+ PGZvbnQgZmFjZT0iYXJpYWwsIHNhbnMtc2VyaWYiPk9uIHRoZSBvdGhlciBoYW5kLCBgYmVmb3Jl KDplYWNoKWAgb3IgdGhlIG5ldyBgYmVmb3JlKDpleGFtcGxlKWAsIHdoaWNoIGlzIHRoZSBkZWZh dWx0IGlmIHlvdSBqdXN0IHdyaXRlIGBiZWZvcmVgLCBpdCBtb3JlIGFjY2VwdGFibGUgdG8gdXNl LiBJdCBpcyBhbHNvIG5vcm1hbGx5IHdoYXQgeW91IHdhbnQuIEl0IGtlZXBzIHN0YXRlIGNsZWFu IGFjcm9zcyBzcGVjcywgaXQgaG9va3MgaW50byB0cmFuc2FjdGlvbnMsIHJzcGVjLW1vY2tzLCBl dGMuIFVubGVzcyB5b3UgaGF2ZSBhIHByb3ZhYmxlLCB3aXRoIHZhbGlkIGRhdGEgc2hvd2luZyBp dCwgY2FzZSB3aGVyZSB0aGlzIGlzJm5ic3A7ZGV0cmltZW50YWwmbmJzcDt0byBwZXJmb3JtYW5j ZSwgaXQncyB0aGUgcHJvcGVyIGNob2ljZS48L2ZvbnQ+PC9kaXY+PGRpdj48c3BhbiBzdHlsZT0i Zm9udC1mYW1pbHk6IGFyaWFsLCBzYW5zLXNlcmlmOyBmb250LXNpemU6IDEzcHg7Ij48YnI+PC9z cGFuPjwvZGl2PjxkaXY+PHNwYW4gc3R5bGU9ImZvbnQtZmFtaWx5OiBhcmlhbCwgc2Fucy1zZXJp ZjsgZm9udC1zaXplOiAxM3B4OyI+UGVyc29uYWxseSwgSSBmZWVsIGBiZWZvcmVgIGFuZCBgbGV0 YCBzaG91bGQgYmUgdXNlZCBzcGFyaW5nbHkuIFRoZXkgc2hvdWxkIGJlIHJlc2VydmVkIGZvciBj YXNlcyB3aGVuIHlvdSBtZWFuIHRvIHN0YXRlOiBfIkFsbCBvZiB0aGUgZm9sbG93aW5nIHNwZWNz IGluIHRoaXMgZ3JvdXAgcmVhbGx5ICoqbXVzdCoqIGhhdmUgdGhpcyAqKnNldHVwIGFjdGlvbioq IGRvbmUgYmVmb3JlIGVhY2ggb2YgdGhlbS4iXzwvc3Bhbj48L2Rpdj48ZGl2PjxzcGFuIHN0eWxl PSJmb250LWZhbWlseTogYXJpYWwsIHNhbnMtc2VyaWY7IGZvbnQtc2l6ZTogMTNweDsiPjxicj48 L3NwYW4+PC9kaXY+PGRpdj48c3BhbiBzdHlsZT0iZm9udC1mYW1pbHk6IGFyaWFsLCBzYW5zLXNl cmlmOyBmb250LXNpemU6IDEzcHg7Ij5Ob3RlIEkgd3JvdGUgInNldHVwIGFjdGlvbiIuIFRoaXMg ZG9lcyBub3QgYXBwbHkgdG8gYW55IGFjdGlvbiByZWdhcmRpbmcgdGhlIG9iamVjdCB1bmRlciB0 ZXN0LiBJIHBlcnNvbmFsbHkgYmVsaWV2ZSB0aG9zZSBzaG91bGQgbm90IGJlIHB1dCBpbiBhbnkg YGJlZm9yZWAgYmxvY2ssIGJ1dCBleHBsaWNpdGx5IGxpc3RlZCBpbiB0aGUgc3BlYyBzbyB0aGF0 IGl0IGlzIGNsZWFyIHdoYXQgeW91IGFyZSB0ZXN0aW5nLCB2ZXJzdXMgd2hhdCBpcyBzZXR0aW5n IHVwIHRoZSB0ZXN0aW5nIGVudmlyb25tZW50LiBMZXQgbWUgc2hhcmUgYSBjb2RlIHNhbXBsZSBl eHBsYWluaW5nIHRoaXMgdmlldzo8L3NwYW4+PC9kaXY+PGRpdj48c3BhbiBzdHlsZT0iZm9udC1m YW1pbHk6IGFyaWFsLCBzYW5zLXNlcmlmOyBmb250LXNpemU6IDEzcHg7Ij48YnI+PC9zcGFuPjwv ZGl2PjxkaXY+PHNwYW4gc3R5bGU9ImZvbnQtZmFtaWx5OiBhcmlhbCwgc2Fucy1zZXJpZjsgZm9u dC1zaXplOiAxM3B4OyI+YGBgcnVieTwvc3Bhbj48L2Rpdj48ZGl2PjxkaXYgc3R5bGU9IiI+PGZv bnQgZmFjZT0iYXJpYWwsIHNhbnMtc2VyaWYiPlJTcGVjLmRlc2NyaWJlIEFycmF5LCAibGlzdCBv ZiBlbGVtZW50cyIgZG88L2ZvbnQ+PC9kaXY+PGRpdiBzdHlsZT0iIj48Zm9udCBmYWNlPSJhcmlh bCwgc2Fucy1zZXJpZiI+Jm5ic3A7IGRlc2NyaWJlICJwb3BwaW5nIHRoZSBhcnJheSIgZG88L2Zv bnQ+PC9kaXY+PGRpdiBzdHlsZT0iIj48Zm9udCBmYWNlPSJhcmlhbCwgc2Fucy1zZXJpZiI+Jm5i c3A7ICZuYnNwOyBsZXQoOmVsZW1lbnRfbGlzdCkgZG88L2ZvbnQ+PC9kaXY+PGRpdiBzdHlsZT0i Ij48Zm9udCBmYWNlPSJhcmlhbCwgc2Fucy1zZXJpZiI+Jm5ic3A7ICZuYnNwOyAmbmJzcDsgWzpm aXJzdCwgOnNlY29uZCwgOmxhc3RdPC9mb250PjwvZGl2PjxkaXYgc3R5bGU9IiI+PGZvbnQgZmFj ZT0iYXJpYWwsIHNhbnMtc2VyaWYiPiZuYnNwOyAmbmJzcDsgZW5kPC9mb250PjwvZGl2PjxkaXYg c3R5bGU9IiI+PGZvbnQgZmFjZT0iYXJpYWwsIHNhbnMtc2VyaWYiPjxicj48L2ZvbnQ+PC9kaXY+ PGRpdiBzdHlsZT0iIj48Zm9udCBmYWNlPSJhcmlhbCwgc2Fucy1zZXJpZiI+Jm5ic3A7ICZuYnNw OyAjIFRoaXMgaXMgdGhlIHNhbWUgYXMgYmVmb3JlKDplYWNoKSBvciBiZWZvcmUoOmV4YW1wbGUp PC9mb250PjwvZGl2PjxkaXYgc3R5bGU9IiI+PGZvbnQgZmFjZT0iYXJpYWwsIHNhbnMtc2VyaWYi PiZuYnNwOyAmbmJzcDsgYmVmb3JlIGRvPC9mb250PjwvZGl2PjxkaXYgc3R5bGU9IiI+PGZvbnQg ZmFjZT0iYXJpYWwsIHNhbnMtc2VyaWYiPiZuYnNwOyAmbmJzcDsgJm5ic3A7IGVsZW1lbnRfbGlz dC5wb3A8L2ZvbnQ+PC9kaXY+PGRpdiBzdHlsZT0iIj48Zm9udCBmYWNlPSJhcmlhbCwgc2Fucy1z ZXJpZiI+Jm5ic3A7ICZuYnNwOyBlbmQ8L2ZvbnQ+PC9kaXY+PGRpdiBzdHlsZT0iIj48Zm9udCBm YWNlPSJhcmlhbCwgc2Fucy1zZXJpZiI+PGJyPjwvZm9udD48L2Rpdj48ZGl2IHN0eWxlPSIiPjxm b250IGZhY2U9ImFyaWFsLCBzYW5zLXNlcmlmIj4mbmJzcDsgJm5ic3A7ICMgTm90ZSB3ZSBhcmUg KipjYW5ub3QqKiB1c2UgdGhlIHNhbWUgc2V0dXAgc3RhdGUgc2luY2Ugd2UgbmVlZCB0byBjYXB0 dXJlPC9mb250PjwvZGl2PjxkaXYgc3R5bGU9IiI+PGZvbnQgZmFjZT0iYXJpYWwsIHNhbnMtc2Vy aWYiPiZuYnNwOyAmbmJzcDsgIyB0aGUgdmFsdWU8L2ZvbnQ+PC9kaXY+PGRpdiBzdHlsZT0iIj48 Zm9udCBmYWNlPSJhcmlhbCwgc2Fucy1zZXJpZiI+Jm5ic3A7ICZuYnNwOyBpdCAicmV0dXJucyB0 aGUgbGFzdCB2YWx1ZSBvZiB0aGUgYXJyYXkiIGRvPC9mb250PjwvZGl2PjxkaXYgc3R5bGU9IiI+ PGZvbnQgZmFjZT0iYXJpYWwsIHNhbnMtc2VyaWYiPiZuYnNwOyAmbmJzcDsgJm5ic3A7IGV4cGVj dChbOmZpcnN0LCA6c2Vjb25kLCA6bGFzdF0ucG9wKS50byBlcSg6bGFzdCk8L2ZvbnQ+PC9kaXY+ PGRpdiBzdHlsZT0iIj48Zm9udCBmYWNlPSJhcmlhbCwgc2Fucy1zZXJpZiI+Jm5ic3A7ICZuYnNw OyBlbmQ8L2ZvbnQ+PC9kaXY+PGRpdiBzdHlsZT0iIj48Zm9udCBmYWNlPSJhcmlhbCwgc2Fucy1z ZXJpZiI+PGJyPjwvZm9udD48L2Rpdj48ZGl2IHN0eWxlPSIiPjxmb250IGZhY2U9ImFyaWFsLCBz YW5zLXNlcmlmIj4mbmJzcDsgJm5ic3A7ICMgTm90ZSBpdCdzIHZlcnkgdW5jbGVhciB3aGF0IHRo ZSB0ZXN0IGlzIGRvaW5nIGp1c3QgYnkgbG9va2luZyBhdCBpdC4gJm5ic3A7V2U8L2ZvbnQ+PC9k aXY+PGRpdiBzdHlsZT0iIj48Zm9udCBmYWNlPSJhcmlhbCwgc2Fucy1zZXJpZiI+Jm5ic3A7ICZu YnNwOyAjIGFyZSBkZXNjcmliaW5nIGFuIGFjdGlvbiBvbiBhbiBBcnJheSBpbnN0YW5jZS4gWWV0 LCB0aGF0IGlzbid0IGNsZWFyIGZyb208L2ZvbnQ+PC9kaXY+PGRpdiBzdHlsZT0iIj48Zm9udCBm YWNlPSJhcmlhbCwgc2Fucy1zZXJpZiI+Jm5ic3A7ICZuYnNwOyAjIHRoaXMgc3BlYyBhdCBhbGws IHNpbmNlIGFsbCB3ZSBzZWUgaXMgdGhhdCB3ZSBjaGVjayB0aGUgYXJyYXkgZWxlbWVudHM8L2Zv bnQ+PC9kaXY+PGRpdiBzdHlsZT0iIj48Zm9udCBmYWNlPSJhcmlhbCwgc2Fucy1zZXJpZiI+Jm5i c3A7ICZuYnNwOyBpdCAibXV0YXRlcyB0aGUgYXJyYXkgcmVtb3ZpbmcgdGhlIGxhc3QgZWxlbWVu dCIgZG88L2ZvbnQ+PC9kaXY+PGRpdiBzdHlsZT0iIj48Zm9udCBmYWNlPSJhcmlhbCwgc2Fucy1z ZXJpZiI+Jm5ic3A7ICZuYnNwOyAmbmJzcDsgZXhwZWN0KGVsZW1lbnRfbGlzdCkudG8gZXEoWzpm aXJzdCwgOnNlY29uZF0pPC9mb250PjwvZGl2PjxkaXYgc3R5bGU9IiI+PGZvbnQgZmFjZT0iYXJp YWwsIHNhbnMtc2VyaWYiPiZuYnNwOyAmbmJzcDsgZW5kPC9mb250PjwvZGl2PjxkaXYgc3R5bGU9 IiI+PGZvbnQgZmFjZT0iYXJpYWwsIHNhbnMtc2VyaWYiPiZuYnNwOyBlbmQ8L2ZvbnQ+PC9kaXY+ PGRpdiBzdHlsZT0iIj48Zm9udCBmYWNlPSJhcmlhbCwgc2Fucy1zZXJpZiI+ZW5kPC9mb250Pjwv ZGl2PjwvZGl2PjxkaXY+PHNwYW4gc3R5bGU9ImZvbnQtZmFtaWx5OiBhcmlhbCwgc2Fucy1zZXJp ZjsgZm9udC1zaXplOiAxM3B4OyI+YGBgPC9zcGFuPjwvZGl2PjxkaXY+PHNwYW4gc3R5bGU9ImZv bnQtZmFtaWx5OiBhcmlhbCwgc2Fucy1zZXJpZjsgZm9udC1zaXplOiAxM3B4OyI+PGJyPjwvc3Bh bj48L2Rpdj48ZGl2PjxzcGFuIHN0eWxlPSJmb250LWZhbWlseTogYXJpYWwsIHNhbnMtc2VyaWY7 IGZvbnQtc2l6ZTogMTNweDsiPkkndmUgc2VlbiB0aGUgYWJvdmUgY29kZSAiY2xlYW5lZCB1cCIg dG8gYmUgbW9yZSBleHByZXNzaXZlIGJ5IGNvbnZlcnRpbmcgaXQgaW50byBzb21ldGhpbmcgbGlr ZTo8L3NwYW4+PC9kaXY+PGRpdj48c3BhbiBzdHlsZT0iZm9udC1mYW1pbHk6IGFyaWFsLCBzYW5z LXNlcmlmOyBmb250LXNpemU6IDEzcHg7Ij48YnI+PC9zcGFuPjwvZGl2PjxkaXY+PGZvbnQgZmFj ZT0iYXJpYWwsIHNhbnMtc2VyaWYiPmBgYHJ1Ynk8L2ZvbnQ+PC9kaXY+PGRpdj48Zm9udCBmYWNl PSJhcmlhbCwgc2Fucy1zZXJpZiI+PGRpdj5SU3BlYy5kZXNjcmliZSBBcnJheSwgImxpc3Qgb2Yg ZWxlbWVudHMiIGRvPC9kaXY+PGRpdj4mbmJzcDsgZGVzY3JpYmUgInBvcHBpbmcgdGhlIGFycmF5 IiBkbzwvZGl2PjxkaXY+Jm5ic3A7ICZuYnNwOyBsZXQoOmFycmF5X3dpdGhfZWxlbWVudHMpIGRv PC9kaXY+PGRpdj4mbmJzcDsgJm5ic3A7ICZuYnNwOyBbOmZpcnN0LCA6c2Vjb25kLCA6bGFzdF08 L2Rpdj48ZGl2PiZuYnNwOyAmbmJzcDsgZW5kPC9kaXY+PGRpdj48YnI+PC9kaXY+PGRpdj4mbmJz cDsgJm5ic3A7ICMgVGhpcyBpcyBpZGlvbSBpcyBvZnRlbiB2aWV3ZWQgYXMgYW4gaW1wbGljaXQg YGJlZm9yZWAuPC9kaXY+PGRpdj4mbmJzcDsgJm5ic3A7ICM8L2Rpdj48ZGl2PiZuYnNwOyAmbmJz cDsgIyBFeGNlcHQsIGl0J3Mgbm90LiBJdCdzIG9ubHkgcnVuIG9uY2UgeW91IHNlbmQgdGhlIGBl bGVtZW50X2xpc3RgIG1lc3NhZ2UuPC9kaXY+PGRpdj4mbmJzcDsgJm5ic3A7ICMgSWYgeW91IGRv IHRoaXMgaW4gdGhlIHdyb25nIHNwb3QgdGhlbiBpdCBtYXkgbGVhZCB0byBzdXJwcmlzaW5nIHRl c3RzLjwvZGl2PjxkaXY+Jm5ic3A7ICZuYnNwOyAjPC9kaXY+PGRpdj4mbmJzcDsgJm5ic3A7ICMg VGhpcyBvZnRlbiBsZWFkcyBwZW9wbGUgdG8gdXNlIGJhbmcgZm9ybSBgc3ViamVjdCFgLCB3aGlj aCBpczwvZGl2Pjxka
--
You received this message because you are subscribed to the Google Groups "rspec" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rspec+un...@googlegroups.com.
To post to this group, send email to rs...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/rspec/74f53f65-4389-4456-b8e6-f18ba532e745%40googlegroups.com.
...