RSpec: What is the difference between let and a before block?

What is the difference between let and before block in RSpec?

And when to use each?

What would be a good approach (even if not before) in the example below?

 let(:user) { User.make !} let(:account) {user.account.make!} before(:each) do @user = User.make! @account = @user.account.make! end 

I have studied this one https://stackoverflow.com/a/16736/

But is it good to define let for associations like above?

+73
ruby-on-rails unit-testing rspec
May 12 '11 at 6:50
source share
4 answers

People seem to have explained some of the basic ways in which they differ, but are not accounted for before (: all) and do not explain why they should be used.

I believe that instance variables do not have a place in the vast majority of specifications, partly because of the reasons mentioned in this answer , so I will not mention them as an option here.

let blocks

The code inside the let block is executed only if there is a link, lazy loading means that the ordering of these blocks does not matter. This gives you more power to reduce re-tuning through your specifications.

One (extremely contrived and small) example:

 let(:person) { build(:person) } subject(:result) { Library.calculate_awesome(person, has_moustache) } context 'with a moustache' do let(:has_moustache) { true } its(:awesome?) { should be_true } end context 'without a moustache' do let(:has_moustache) { false } its(:awesome?) { should be_false } end 

You can see that has_moustache is defined differently in each case, but there is no need to repeat the definition of subject . It is important to note that the last let block defined in the current context will be used. This is useful for setting the default for most specifications, which can be overwritten if necessary.

For example, checking the return value of calculate_awesome , if the person model with top_hat , is set to true, but the mustache will not:

 context 'without a moustache but with a top hat' do let(:has_moustache) { false } let(:person) { build(:person, top_hat: true) } its(:awesome?) { should be_true } end 

Another thing worth noting about let blocks is that they should not be used if you are looking for something that has been stored in the database (i.e. Library.find_awesome_people(search_criteria) ) since they will not be stored in the database if they were no longer links. let! Blocks or before should be used here.

Also, never ever use before to start let blocks execute, this is what let! made for!

let be! blocks

let! blocks are executed in the order in which they are defined (as before the block). One major difference before blocks is that you get an explicit reference to this variable, instead of having to return to instance variables.

As with let blocks, if multiple let! Blocks defined with the same name, the very last one is what will be used during execution. The main difference is that let! Blocks will be executed several times if they are used like this, whereas the let block will execute only the last time.

before (: each) blocks

before(:each) is the default value before the block, so you can call it before {} instead of specifying the full before(:each) {} every time.

I prefer using before blocks in several basic situations. I will use up blocks if:

  • I use mockery, stubbing or doubleles
  • There is some kind of reasonable size setting (usually this is a sign that your factory features are set incorrectly).
  • There are a number of variables that I do not need to refer directly to, but are required for installation
  • I am writing functional controller tests in rails and I want to execute a specific request for testing (ie before { get :index } ). Even if you can use subject for this in many cases, sometimes it becomes more explicit if you don't need a link.

If you find yourself writing large before blocks for your specifications, check your plants and make sure you fully understand the features and their flexibility.

to (: all) blocks

They are executed only once, before the specifications in the current context (and its children). They can be used with great advantage if they are written correctly, as there are certain situations that can shorten the execution and effort.

One example (which is unlikely to affect run time at all) is making fun of the ENV variable for a test that you only ever need to do.

Hope that helps :)

+116
Mar 26 '13 at 10:01
source share

Almost always, I prefer let . The message you specify indicates that let also faster. However, sometimes when you need to execute several commands, I might use before(:each) because its syntax is clearer when many commands are involved.

In your example, I would prefer to use let instead of before(:each) . Generally speaking, when only some kind of initialization variable is executed, I like to use let .

+25
May 12 '11 at 9:35
source share

The big difference, which was not mentioned, is that the variables defined with let are not created until you name it for the first time. Thus, while the before(:each) block will create all the variables, let allows you to determine the number of variables that you can use for several tests, it does not create them automatically. Without knowing this, your tests may come back to bite yourself if you expect all data to be downloaded in advance. In some cases, you can even determine the number of let variables, and then use the before(:each) block to call each let instance to make sure the data is available to begin with.

+15
Jan 09 '12 at 16:35
source share

It looks like you are using a train driver. Beware, you may see some problems with make! inside the let version (non-bang version) occurring outside the global fixture transaction (if you also use transactional lights), thus distorting the data for your other tests.

+3
Aug 02 2018-11-11T00:
source share



All Articles