I'm pretty new to both Ruby and Selenium and I'm just trying to figure out the best way to build my test harness. I'm using Rake, Rake::TestTask, and Test::Unit to power this. I have a suite that I'd like to run once for each browser/os combination. I can't figure out how to parameterize my tests though, something I've become accustomed to with Junit4 and TestNG.
require 'rake'
require 'rake/testtask'
browsers = ['IE on Windows', 'Firefox on Windows', 'Firefox on Mac', 'Safari on Mac']
task :default => [:run_tasks]
task :create_tasks do
browsers.each do |browser|
Rake::TestTask.new("selenium_units:#{browser}") do |t|
t.libs << "lib"
t.pattern = 'test/*_test.rb'
t.verbose = true
t.warning = true
t.opts = "BROWSER=\"#{browser}\""
end
end
end
task :run_tasks => [:create_tasks]
task :run_tasks => browsers.map { |e| "selenium_units:"+ e }
I'd really like to be able to read that BROWSER= in the setup of my Suites or Cases. Any suggestions or is there plainly a better way of doing this in Ruby?
After a lot of digging through the source I found a not so documented feature. The Test::Unit test runner doesn't do anything besides executing your test cases on the commandline relying on an autorunner to run any case cases of that class specific class. So, when they say anything after -- will be passed as options, they mean command line options, not some variable or parameter.
so...
adding
t.options = '-- -foo -bar=baz'
Will actually pass those into your ARGV and you are expected to process them manually. Taking those args and throwing them into a factory class to grab the appropriate Selenium instance will work for me.
This may not be what you are asking. But I've solved the exact same problem in Python and blogged about it in
Parameterizing Your Tests. I am sure you can use the same approach in Ruby, not sure how it compares with what you've done. I am interested to know how others have solved this problem though, and haven't found much documentation.
Related
I'm working on some unit tests. One of them use a specific configuration variable as set in my application MyBigApp::Env which looks like:
{:country=>'uk', :another_hosts=>["192.168.99.105"]}
So I access it with MyBigApp::Env.country
However in my unit test I want that country for the test become something.
Using rspec I've seen stub but can't get it to work - any ideas where I'm going wrong:
MyBigApp::Env.stub(:[]).with('country').and_return('gr')
Also tried this (as above shows deprecated):
allow(MyBigApp::Env).to receive('country').and_return('gr')
Infact as a test, I also tried:
my_hash = {:uri=>nil}
allow(my_hash).to receive(:[]).with(:uri).and_return('Over written!')
p my_hash
and that didnt update either - it just returned {:uri=>nil}
As a workaround, at the moment I'm having to save the env var in a temp var in the before(each) block then return it back to the original in the after(each). This feels really risky to me. I'm thinking imagine the service running and someone runs unit tests it could effect the end user in that small instance the test is running.
Any help would be appreciated.
Thanks
Yes it possible, but keep in mind that stub only works when you trigger/call the method that you stubbed/mocked
my_hash = {:uri=>nil}
allow(my_hash).to receive(:[]).with(:uri).and_return('Over written!')
p my_hash[:url] # it will be 'Over written!'
This works for me:
my_hash = {:uri=>nil}
allow(my_hash).to receive(:[]).with(:uri).and_return('Over written!')
expect(my_hash[:uri]).to eq "Over written!"
In your sample test case, you are just calling p my_hash which doesn't actually call the [] method.
In terms of why this isn't working with MyBigApp::Env, well, that really depends on what class it is. Possible whatever method .country is doesn't actually call [].
Really, if you call MyBigApp::Env['country'] and stub MyBigApp::Env to receive [] with 'country', it should work.
In regards to your concern about changing your running application's behavior from the tests ... what kind of tests are these?! Running unit tests against a live production application would be very odd. How do you imagine it would change your production app's code? The Env hash just lives in memory right?
Anyway, you should never have to worry about your tests changing the experience for an 'end user'. Always run tests on a completely quarantined envionment, meaning don't use the same database. Actually, the test database is usually wiped after each test.
Just wanted to suggest a non-stubbing alternative. For example:
def code_under_test
key = 'country'
# ... maybe lots of code
value = MyBigApp::Env[key] # deep inside some classes
# ... lots more code
"This is the #{value}"
end
MyBigApp::Env is hard-coded deep in the code, and the need for stubbing reveals that dependency, and the benefits of OOP encapsulation are lost.
It'd be much easier if this were the case:
def code_under_test(config_vars = MyBigApp::Env)
"This is the #{config_vars['country']}"
end
it 'should return my country value' do
value = previous_code_under_test('country' => 'TEST VALUE')
expect(value).to eq("This is the TEST VALUE")
end
No stubbing required, just plain old method calls.
I am writing a Ruby gem that accesses the features of a web based API. I need to set up an object which will be initialized and log into the API just once for each time the tests are run. before(:all) is still excessive because it will run once for every describe block, and what I want is something that universally sets up once for all of the test files.
UPDATE
Just as a follow up, to make the object I was using available in the tests, I had to add a setting to the rspec config like this
config.add_setting :client
config.before(:suite) do
RSpec.configuration.client = TDAmeritradeApi::Client.new
RSpec.configuration.client.login
end
And then in the describe blocks I do this:
let(:client) { RSpec.configuration.client }
I believe you are looking for before(:suite) and you can use it in the config section of your spec_helper.rb.
RSpec.configure do |config|
config.before(:suite) do
# API login
end
end
You can use before(:suite) to run a block of code before any example groups are run. This should be declared in RSpec.configure
Source: http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/Hooks
I need to alter env variables eg URLS, depending on the environment (alpha, beta, and public/ops) in which the tests are to be run. Are "contexts" within RSpec that allow for this?
I am not sure if I understand the problem you try to solve. Normally RSpec runs in only in the test environment, not in multople environments.
Your test environment is defined in config/environments/test.rb. In than file you define everything that is valid for all kind of tests. For example you will never ever want to send emails from your test suite. Therefore you will find something like this in that file:
config.action_mailer.delivery_method = :test
If there is something that you want to change in your specs (if DelayedJobs should be available or not in the following example), then there are multiple ways to do so. The one I see most is to stub objects to return the values you need for a certain test:
describe 'a complex operation' do
context 'with DelayedJobs' do
# acts like DelayedJobs are configured to run in background
before { Delayed::Worker.stub(:delay_jobs => true) }
it 'creates a DelayedJob' do
...
end
end
context 'without DelayedJobs' do
# acts like DelayedJobs are configured to run immediately
before { Delayed::Worker.stub(:delay_jobs => false) }
it 'calls the complex query' do
...
end
end
end
What's the best practice to store variables for Cucumber tests?
I'm using Cucumber and Capybara to test a Java application.
In the application there are many types of users and other data that I need to pass into my Capybara step definitions. So my question is what is the best practice to store these variables/users IDs?
I do have some variables (#instance_variabless) that I've stored all in the env.rb file.
But is there a more clean/elegant way to do this? Like have a dedicated file just for variables that I can call, if so how would such a file be setup?
Please provide examples of solutions?
You can use hooks for set-up and tear-down in hooks.rb file:
#user_ids_and_passwords = YAML.load(File.open('features/config/users.yml'))
#my_heavy_object = HeavyObject.new
#my_heavy_object.do_it
at_exit do
#my_heavy_object.undo_it
end
AfterConfiguration do |config|
puts "Features dwell in #{config.feature_dirs}"
#instance_variable = 'my variable'
end
Before('#some_features') do
#feature_instance_variable = 'something else'
end
So I've been trying to figure this out, and the best solution I can came up with his global variables - but that seems so dirty and 1974 - Am I missing a feature of Rake/ Test::Unit?
I have a Rake file in which I'm running tests:
Rake::TestTask.new(:test) do |t|
t.test_files = FileList['test_*.rb']
end
and test_1.rb has something like this:
require "test/unit"
class TestStuff < Test::Unit::TestCase
def setup
#thingy = Thing.New(parameter1, parameter2)
end
def test_this_thing
#thing.do()
end
end
My problem is, Thing.new() requires arguments, and those arguments are specific to my environment. In reality, I'm running Selenium-WebDriver, and I want to pass in a browser type, and a url, etc... sometimes I want ff, othertimes I want chrome... sometimes this URL, sometimes that... depending on the day, etc.
The simplest thing seems to do something like:
#all that rake stuff
$parameter1 = x
$parameter2 = y
and then make Thing.new() look up my global vars:
#thingy = Thing.New($parameter1, $parameter2)
This seems sloppy.. and it just doesn't feel right to me. I'm still trying to get this 'test harness' up and running, and want to do it right the first time. That's why I chose Rake, based on a lot of other feedback.
Keep in mind, I'll probably have 100's of tests, ultimately, and they'll all need to do get this information... I thought Rake was good at making sure all of this was easy, but it doesn't seem to be.
Where did I go wrong?
I have used YAML files to store my configuration (browser config, environments including URLs, etc).
You can also use an environmental variable to define simple configurations. You can access environmental variables via ENV['foobar'] in Ruby.
So, for example, my browser call might look like this inside my setup method:
driver = Selenium::WebDriver.for (ENV['SWD_BROWSER'] || "firefox").to_sym
and in my Rake file (or in the shell console) define the environmental variable to use.