Using variable values within different scenarios in a feature, capybara - variables

I have a feature with 4 scenarios. I would like to use the value of 1 variable I set in scenario 1 across different steps and in Scenario 2.
I use $ but this is not set. I am assuming $ value remains the same across a feature
When(/^the user goes to manageusers, picks one of the secondary users$/) do
click_link "Admin"
click_link "Manage Users"
emailofuser=ENV["email"].to_s
atpos = emailofuser.index('#')
emailofuser = emailofuser[0,atpos]
page.body.to_s.scan(/<td>(.*?)#ABC.com<\/td>/).flatten().each do |w|
if "#{w}" != emailofuser
$secondaryUserEmail = "#{w}" + "#ABC.com"
break
end
end
end
When(/^the secondary user logs in with password "([^"]*)"$/) do |arg|
if getURL != URI.parse(current_url)
visit getURL
end
find(:xpath,"//input[#id='user_email']").set($secondaryUserEmail )
fill_in "user_password", :with => arg
click_button "Sign in"
end
In the Above Step, the steps are in 1 scenario in a feature file and I also have the same step secondary user in Scenario 2 within a feature. the variable $secondaryuserEmail some times does not get set and login as a secondary user fails.
Whats the best way for me to declare variables that I can access across steps within a scenario and across scenarios within a feature.

You should find out why $secondaryuserEmail does not get set. That sounds like a bug somewhere in the app you're testing. If it's not a bug, you should try to handle the exception.
To your original question, it might be a good idea to set variables in helper methods, then call these methods to access them using instance variables. Most people generally recommend against sharing variables across scenarios but I've used helper methods in order to store variables throughout my specs that normally go unchanged.

Related

Custom strategy for warden not getting called

I am trying to use a different warden strategy to authenticate my action cable end points.
But the strategy is not getting called. I tried to place warden.authenticate!(:action_cable_auth) in a controller to test but none of the debug statements are getting printed on console.
Below are the relevant part of the code.
config/initializers/warden.rb
Warden::Strategies.add(:action_cable_auth) do
def valid?
#check if its a websocket request & for action cable?
#Rails.logger.error request.inspect
p 'checking if strategy is valid?'
true
end
def authenticate!
p 'unauthenticate the user'
fail!('user not active')
end
end
in my controller
warden.authenticate!(:action_cable_auth)
Assuming that you are setting your initializer in the proper place, please recall that if your session is already instantiated somewhere else (for example if you authenticate the user at the point your action is being called, then your strategy will never be called.
This is basically how warden works: if some valid? strategy returns a success! then no other will be called as soon as any authenticate! method in the list of strategies is successful.
Please also be sure that if you want your strategy up the list of strategies to check you may need to also shift it up on the list, such as:
manager.default_strategies(scope: :user).unshift(:action_cable_auth)
Where the manager is your Warden::Manager instance. The scope may also be optional (this is an example where the user scope is used alongside Devise), but you may check your instance .default_strategies to figure out where it is and where you want it.

Is it possible to include the same few lines of scenario at the beginning of a new one?

I have a 138 tests long scenario that allows me to test if a workflow works as it should.
However, I'm not a huge fan of having to repeat the same 6 lines that allows me to log in as an administrator, which are:
Given I am on "user/login"
And I fill in "admin#admin.com" for "name"
And I fill in "admin" for "pass"
And I press "Log in"
Then I should get a "200" HTTP response
And I should see "Dashboard"
I repeat this part 6 times so far, and I'm planning on needing to add this a few more time for some other tests.
So my question is the following: is there a way, through the FeatureContext file or any other way, to make these lines repeat?
Thank you in advance
So this is how I did:
Instead of calling Gherkin sentence one after the other, I parsed the vendor/ directory to find examples of how sentences where made.
my function to that connects me looks like the following:
/**
* #throws ElementNotFoundException
* #throws Exception
* #Given I am the administrator
*/
public function iAmTheAdministrator(){
$this->visitPath('/user/login');
$element = $this->getSession()->getPage();
$element->fillField('name', 'admin#admin.com');
$element->fillField('pass', 'admin');
$element->pressButton("Se connecter");
$this->assertSession()->pageTextContains('Dashboard');
}
This is pretty straight forward, and works well
There are 2 ways I know of that repeats steps. One is with background steps and the other involves running snippets of steps.
Background steps run at the start of each scenario.
Background:
Given I have done this
And this other thing
Scenario: Do stuff
When I do this
Then stuff should happen
This only works if all tests have the same starting procedure...
Snippets run whenever you call them which I assume you would rather want
Given I have logged in as an administrator
Step definition:
Given(/^I have logged in as an administrator$/) do
steps %{
Given I am on "user/login"
And I fill in "admin#admin.com" for "name"
And I fill in "admin" for "pass"
And I press "Log in"
Then I should get a "200" HTTP response
And I should see "Dashboard"
}
end
This allows you to use only one step which you can call at any time to run multiple steps
Hope this helps.

Difference between with and without sudo() in Odoo

What is different between:
test = self.env['my.example'].sudo().create({'id':1, 'name': 'test'})
test = self.env['my.example'].create({'id':1, 'name': 'test'})
All example work, but what is the advantages when using sudo()?
Odoo 8–12
Calling sudo() (with no parameters) before calling create() will return the recordset with an updated environment with the admin (superuser) user ID set. This means that further method calls on your recordset will use the admin user and as a result bypass access rights/record rules checks [source]. sudo() also takes an optional parameter user which is the ID of the user (res.users) which will be used in the environment (SUPERUSER_ID is the default).
When not using sudo(), if the user who calls your method does not have create permissions on my.example model, then calling create will fail with an AccessError.
Because access rights/record rules are not applied for the superuser, sudo() should be used with caution. Also, it can have some undesired effects, eg. mixing records from different companies in multi-company environments, additional refetching due to cache invalidation (see section Environment swapping in Model Reference).
Odoo 13+
Starting with Odoo 13, calling sudo(flag) will return the recordset in a environment with superuser mode enabled or disabled, depending if flag is True or False, respectively. The superuser mode does not change the current user, and simply bypasses access rights checks. Use with_user(user) to actually switch users.
You can check the comments on sudo in Odoo code at odoo -> models.py -> def sudo().
Returns a new version of this recordset attached to the provided
user.
By default this returns a ``SUPERUSER`` recordset, where access
control and record rules are bypassed.
It is same as:
from odoo import api, SUPERUSER_ID
env = api.Environment(cr, SUPERUSER_ID, {})
In this example we pass SUPERUSER_ID in place of uid at the time of creating a Enviroment.
If you are not use Sudo() then the current user need permission to
create a given object.
.. note::
Using ``sudo`` could cause data access to cross the
boundaries of record rules, possibly mixing records that
are meant to be isolated (e.g. records from different
companies in multi-company environments).
It may lead to un-intuitive results in methods which select one
record among many - for example getting the default company, or
selecting a Bill of Materials.
.. note::
Because the record rules and access control will have to be
re-evaluated, the new recordset will not benefit from the current
environment's data cache, so later data access may incur extra
delays while re-fetching from the database.
The returned recordset has the same prefetch object as ``self``.

Why does MembershipProvider.GetUser consume so many resources how to ensure it uses the EF db context?

In our web application, we observed the following:
GetUser/CreatMembershipEntities/ExplicitLoadFromAssembly seems quite expensive.
Also noticing that CreateEntityConnection is being called - EntityFramework?
I'm not entirely convinced that EF was configured correctly for this application. If it was, and was in use, I wouldn't expect to see new connections to be initiated for every call - yes/no?
Is a way to streamline this to avoid some major code refactoring?
The use of System.Web.Security.Membership.GetUser() seems like a biggie here. Instead of using the MembershipProvider to create users, what about just executing a sproc that does the same things.
I have found the following code that is ridiculous, as far as I am concerned - in causing a new call for each user until a unique one can be generated:
For i As Integer = 1 To Integer.MaxValue
'Generate unique username
If System.Web.Security.Membership.GetUser(userName & i) Is Nothing Then
'Increment value until no duplicate username found
userName = userName & i
Exit For
End If
Next
-- UPDATE--
I have modified the question slightly...
We were able to run up to 20 users then the IIS server would tank. Does the GetUser() method create a brand new connection every time? It looks like it, based on the results. How can I ensure that this GetUser() thing is actually using the db context, rather than spinning up its own connections?

change rails environment in the mid of testing

How to change the environment variable of rails in testing
You could do
Rails.stub(env: ActiveSupport::StringInquirer.new("production"))
Then Rails.env, Rails.development? etc will work as expected.
With RSpec 3 or later you may want to use the new "zero monkeypatching" syntax (as mentioned by #AnkitG in another answer) to avoid deprecation warnings:
allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new("production"))
I usually define a stub_env method in a spec helper so I don't have to put all that stuff inline in my tests.
An option to consider (as suggested in a comment here) is to instead rely on some more targeted configuration that you can set in your environment files and change in tests.
Rspec 3 onwards you can do
it "should do something specific for production" do
allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new("production"))
#other assertions
end
Sometimes returning a different environment variable can be a headache (required production environment variables, warning messages, etc).
Depending on your case, as an alternate you may be able to simply return the value you need for your test to think it's in another environment. Such as if you wanted Rails to believe it is in production for code that checks Rails.env.production? you could do something like this:
it "does something specific when in production" do
allow(Rails.env).to receive(:production?).and_return(true)
##other assertions
end
You could do the same for other environments, such as :development?, :staging?, etc. If you don't need the full capacity of returning a full environment, this could be another option.
As a simpler variation on several answers above, this is working for me:
allow(Rails).to receive(:env).and_return('production')
Or, for as I'm doing in shared_examples, pass that in a variable:
allow(Rails).to receive(:env).and_return(target_env)
I suspect this falls short of the ...StringInquirer... solution as your app uses additional methods to inspect the environment (e.g. env.production?, but if you code just asks for Rails.env, this is a lot more readable. YMMV.
If you're using something like rspec, you can stub Rails.env to return a different value for the specific test example you're running:
it "should log something in production" do
Rails.stub(:env).and_return('production')
Rails.logger.should_receive(:warning).with("message")
run_your_code
end