rspec way for passing variable between multiple contexts
Asked Answered
W

3

5

I was wondering what would be the best way to pass variable between multiple contexts (or multiple its) in rspec but without using global variables?

For example, I have this:

describe "My test" do
    let(:myvar) { @myvar = 0 }

    context "First test pass" do
        it "passes" do
            myvar = 20
            expect(myvar).to eq(20)
        end
    end

    context "Second test pass" do
        it "passes" do
            expect(myvar).to eq(20)
        end
    end
end

Now, obviously, this will not work with let because with new context, myvar variable will be back on initial state which is = 0. I would need mechanism to "cache state" between two contexts which would in turn give me value of myvar = 20 in second context

Any opinions, suggestions and improvements are welcome. Thanks

Windham answered 12/12, 2014 at 16:11 Comment(1)
Simply not passing variable between contexts is better. If you need a context, create it in the context block.Delano
J
8

Another simple way, would be to define a 'local variable' in describe context. the 'local variable' would live throughout the describe, and any changes during run time would effect it, and so change it.

For example

describe 'tests' do
    context 'Sharing a variable across tests' do
        var = 1
        puts var

        it "it one. var = #{var}" do
            var = var*2
            puts var
        end
        it "it two" do
            puts var
        end
    end
end 

Output

1
2
1
Juggle answered 31/5, 2016 at 12:6 Comment(0)
M
2
  1. What happens is not what you think happens.
  2. What you want to happen break "unit testing" as a methodology.

Let me explain #2 first - unit testing test cases should be able to work in isolation, which means that they should work when run together, when run apart, and in any order... so much so that some unit testing frameworks (like the default one in ) run test cases in parallel...

As for #1 - when you write myvar = 20 you are not assigning a value to let(:myvar) { @myvar = 0 }, you simply create a local variable, which will override all calls to myvar within the method, but will not be available outside the method (myvar will return 0).

Even if you would have set @myvar = 20 (unless you do it before you call myvar for the first time) instead, myvar would still return 0, since the let function is using a memento pattern, which means it is called once, and subsequent calls return the value originally returned (in this case 0):

puts myvar
@myvar = 20
puts myvar

# => 0
# => 0
Memberg answered 12/12, 2014 at 16:20 Comment(3)
So in nutshell, let gives us possibility to define starting state (whether it is object or simple type) which can be used through subsequent contexts but not altered?Windham
@BakirJusufbegovic - no, it is re-created for each test case, but not within a test case (more than once)Memberg
This means let is re-created between examples (it's) but not within a same example (when used multiple times). Guess let cannot be used for mechanism that I need and it is ability to "cache state" of some variable between examples. I understand that unit testing should not work in that way (tests should be independent) but I'm using RSpec for functional tests where these examples (or I would rather call them test steps) execute in sequential order and often output of some example will be needed as input to next oneWindham
B
0

I just ran into this same problem. How I solved it was by using factory_girl gem.

Here's the basics:

create a factory:

require 'factory_girl'
require 'faker' # you can use faker, if you want to use the factory to generate fake data

FactoryGirl.define do
  factory :generate_data, class: MyModule::MyClass do
    key 100 # doesn't matter what you put here, it's just a placeholder for now
    another_key 'value pair'
  end
end

Now after you made the factory you need to make a Model that looks like this:

Module MyModule
  class MyClass
    #for every key you create in your factory you must have a corresponding attribute accessor in the model.
    attr_accessor :key, :another_key

    #you can also place methods here to call from your spec test, if you wish
    # def self.test
        #some test
    # end
  end
end

Now going back to your example you can do something like this:

describe "My test" do
    let(:myvar) { @myvar }

    context "First test pass" do
        it "passes" do
            @myvar.key = 20 #when you do this you set it now from 100 to 20
            expect(@myvar.key).to eq(20)
        end
    end

    context "Second test pass" do
        it "passes" do
            expect(@myvar.key).to eq(20) #it should still be 20 unless you overwrite that variable
        end
    end
end

As stated by others, not proper way of unit testing. But, how should we know if you're unit testing or not. So, I won't judge. Anyways, good luck let us know, if you got some other solution!

Busch answered 3/9, 2015 at 18:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.