Access global variable in ruby unit tests - ruby

I'm having problems using a global variable defined in the my test class, which then is referenced in the libraries file. I'm using Ruby 1.9.3p392 and test-unit 2.5.4.
This is the code that runs the tests:
require 'rubygems'
gem 'test-unit'
require 'test/unit'
require 'ci/reporter/rake/test_unit_loader'
load '../lib/functions.rb'
require 'watir'
class Test_002 < Test::Unit::TestCase
include Functions
class << self
def startup
#browser = Watir::Browser.new :ie
#browser.speed = :fast
end
def shutdown
#browser.close
end
end
def test_001_login
login('some_url', 'some_user', 'some_passw')
end
end
And this is part of the library that contains the login function:
require 'rubygems'
module Functions
def login(url, user, passw)
#browser.goto(url)
...
end
end
This is the output:
Started
E
===============================================================================
Error: test_001_login(Test_002)
NoMethodError: undefined method `goto' for nil:NilClass
(...)
23: end
24:
25: def test_001_login
=> 26: login('some_url', 'some_user', 'some_passw')
27: end
28:
29: end
===============================================================================
Finished in 3.0043 seconds.
1 tests, 0 assertions, 0 failures, 1 errors, 0 pendings, 0 omissions, 0 notifications
0% passed
0.33 tests/s, 0.00 assertions/s
Any ideas?

Your pre-test initialization code is not being run.
The TestUnit framework expects the setup code to be in a method named setup and the teardown code to be in a method named teardown
You have two options either rename the method startup to setup and the method shutdown to teardown, or, if you need setup/shutdown for some other purpose, create delegate methods:
def setup
startup
end
def teardown
shutdown
end

Instance variables (ie #browser) defined in the startup method will not be available to the tests. Based on this old forum thread, http://www.ruby-forum.com/topic/144884, it is by design:
That behavior is by design. The "test isolation" ideal implies you shouldn't need to work too hard to get a clean slate each time a test case starts. Otherwise a test could rely on a #value from the previous test, and could rely on it in a way you don't notice.
Of course any other persistent variable could ruin test isolation. We don't stopand restart Ruby between each test case. But at least when it happens the #instance variables won't be to blame.
One workaround that I have used I have used to use the same browser across tests was to use a class variable (ie ##browser). The startup would look like the following. The other methods would similarly have to be updated to use ##browser.
def startup
##browser = Watir::Browser.new :ie
##browser.speed = :fast
end

Related

No error when running unit test with an expectation on method calls (Ruby/Test::Unit/Mocha)

I have a question regaring Mocha expectations running with the Ruby Test::Unit framework.
My purpose is to verify the methods called when I call a wrapping method.
This test always seem to pass, I don't understand why...
I am expecting an error stating that the method clean has only be called once (with can be verified in the Expectation object).
Why is the following Unit Test not raising an error when I run the following test:
require 'test/unit'
require 'mocha'
require 'mocha/test_unit'
class DoMe
def stop_and_clean
clean
stop
end
def clean
true
end
def stop
true
end
end
class DoMeTest < Test::Unit::TestCase
def test_stop_and_clean
d = DoMe.new
d.expects(:clean).times(5)
assert(d.stop_and_clean)
end
end
It seems to me that expects(:clean).times(5) will not be satisfied, but running the test shows no error. Do I need to make an assertion on the Expectation object ?
exp = d.expects(:clean).times(5)
[...]
assert(that exp is invoked only once)

why is before :save callback hook not getting called from FactoryGirl.create()?

This simple example uses DataMapper's before :save callback (aka hook) to increment callback_count. callback_count is initialized to 0 and should be set to 1 by the callback.
This callback is invoked when the TestObject is created via:
TestObject.create()
but the callback is skipped when created by FactoryGirl via:
FactoryGirl.create(:test_object)
Any idea why? [Note: I'm running ruby 1.9.3, factory_girl 4.2.0, data_mapper 1.2.0]
Full details follow...
The DataMapper model
# file: models/test_model.rb
class TestModel
include DataMapper::Resource
property :id, Serial
property :callback_count, Integer, :default => 0
before :save do
self.callback_count += 1
end
end
The FactoryGirl declaration
# file: spec/factories.rb
FactoryGirl.define do
factory :test_model do
end
end
The RSpec tests
# file: spec/models/test_model_spec.rb
require 'spec_helper'
describe "TestModel Model" do
it 'calls before :save using TestModel.create' do
test_model = TestModel.create
test_model.callback_count.should == 1
end
it 'fails to call before :save using FactoryGirl.create' do
test_model = FactoryGirl.create(:test_model)
test_model.callback_count.should == 1
end
end
The test results
Failures:
1) TestModel Model fails to call before :save using FactoryGirl.create
Failure/Error: test_model.callback_count.should == 1
expected: 1
got: 0 (using ==)
# ./spec/models/test_model_spec.rb:10:in `block (2 levels) in <top (required)>'
Finished in 0.00534 seconds
2 examples, 1 failure
At least for factory_girl 4.2 (don't know since which version it is supported), there is another workwaround through the use of custom methods to persist objects. As it is stated in a response to an issue about it in Github, it is just a matter of calling save instead of save!.
FactoryGirl.define do
to_create do |instance|
if !instance.save
raise "Save failed for #{instance.class}"
end
end
end
Of course it is not ideal because it should be functional in FactoryGirl core, but I think right now it is the best solution and, at the moment, I'm not having conflicts with other tests...
The caveat is that you have to define it in each factory (but for me it wasn't an inconvenient)
Solved.
#Jim Stewart pointed me to this FactoryGirl issue where it says "FactoryGirl calls save! on the instance [that it creates]". In the world of DataMapper, save! expressly does not run the callbacks -- this explains the behavior that I'm seeing. (But it doesn't explain why it works for #enthrops!)
That same link offers some workarounds specifically for DataMapper and I'll probably go with one of them. Still, it would be nice if an un-modified FactoryGirl played nice with DataMapper.
update
Here's the code suggested by Joshua Clayton of thoughtbot. I added it to my spec/factories.rb file and test_model_spec.rb now passes without error. Cool beans.
# file: factories.rb
class CreateForDataMapper
def initialize
#default_strategy = FactoryGirl::Strategy::Create.new
end
delegate :association, to: :#default_strategy
def result(evaluation)
evaluation.singleton_class.send :define_method, :create do |instance|
instance.save ||
raise(instance.errors.send(:errors).map{|attr,errors| "- #{attr}: #{errors}" }.join("\n"))
end
#default_strategy.result(evaluation)
end
end
FactoryGirl.register_strategy(:create, CreateForDataMapper)
update 2
Well. perhaps I spoke too soon. Adding the CreateForDataMapper fixes that one specific test, but appears to break others. So I'm un-answering my question for now. Someone else have a good solution?
Use build to build your object, then call save manually...
t = build(:test_model)
t.save

Before/After Suite when using Ruby MiniTest

Is there an alternative to RSpec's before(:suite) and after(:suite) in MiniTest?
I suspect that a custom test runner is in order, however I cannot imagine it is not a common requirement, so somebody has probably implemented in. :-)
There are setup() and teardown() methods available. The documentation also lists before() and after() as being available.
Edit: Are you looking to run something before each test or before or after the whole suite is finished?
As noted above in Caley's answer and comments, MiniTest::Unit contains the function after_tests. There is no before_tests or equivalent, but any code in your minitest_helper.rb file should be run before the test suite, so that will do the office of such a function.
Caveat: Still relatively new at Ruby, and very new at Minitest, so if I'm wrong, please correct me! :-)
To get this to work with the current version of Minitest (5.0.6) you need to require 'minitest' and use Minitest.after_run { ... }.
warn "MiniTest::Unit.after_tests is now Minitest.after_run. ..."
https://github.com/seattlerb/minitest/blob/master/lib/minitest.rb
https://github.com/seattlerb/minitest/blob/master/lib/minitest/unit.rb
To run code before each test, use before. You're operating here in the context of an instance, possibly of a class generated implicitly by describe, so instance variables set in before are accessible in each test (e.g. inside an it block).
To run code before all tests, simply wrap the tests in a class, a subclass of MiniTest::Spec or whatever; now, before the tests themselves, you can create a class or module, set class variables, call a class method, etc., and all of that will be available in all tests.
Example:
require "minitest/autorun"
class MySpec < MiniTest::Spec
class MyClass
end
def self.prepare
puts "once"
##prepared = "prepared"
##count = 0
end
prepare
before do
puts "before each test"
#local_count = (##count += 1)
end
describe "whatever" do
it "first" do
p MyClass
p ##prepared
p #local_count
end
it "second" do
p MyClass
p ##prepared
p #local_count
end
end
end
Here's the output, along with my comments in braces explaining what each line of the output proves:
once [this code, a class method, runs once before all tests]
Run options: --seed 29618 [now the tests are about to run]
# Running tests:
before each test [the before block runs before each test]
MySpec::MyClass [the class we created earlier is visible in each test]
"prepared" [the class variable we set earlier is visible in each test]
1 [the instance variable from the before block is visible in each test]
before each test [the before block runs before each test]
MySpec::MyClass [the class we created earlier is visible in each test]
"prepared" [the class variable we set earlier is visible in each test]
2 [the instance variable from the before block is visible each test]
(Note that I do not mean this output to imply any guarantee about the order in which tests will run.)
Another approach is to use the existing before but wrap code to run only once in a class variable flag. Example:
class MySpec < MiniTest::Spec
##flag = nil
before do
unless ##flag
# do stuff here that is to be done only once
##flag = true
end
# do stuff here that is to be done every time
end
# ... tests go here
end
One simple way to do this is to write a guarded class method, and then call that in a begin.
A Minitest::Spec example:
describe "my stuff" do
def self.run_setup_code
if #before_flag.nil?
puts "Running the setup code"
#before_flag = true
end
end
before do
self.class.run_setup_code
end
it "will only run the setup code once" do
assert_equal 1, 1
end
it "really only ran it once" do
assert_equal 1,1
end
end
...to get
Run options: --seed 11380
# Running:
Running the setup code
..
Finished in 0.001334s, 1499.2504 runs/s, 1499.2504 assertions/s.
2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
You can just place the code outside of the class.
This is what I do to have a banner.
require 'selenium-webdriver'
require 'minitest/test'
require 'minitest/autorun'
class InstanceTest < Minitest::Test
def setup
url = ARGV.first
#url = self.validate_instance(url)
#driver = Selenium::WebDriver.for :firefox
end
Nice thing about minitest is its flexibility. I've been using a custom MiniTest Runner with a +before_suite+ callback. Something like in this example - Ruby Minitest: Suite- or Class- level setup?
And then tell minitest to use the custom runner
MiniTest::Unit.runner = MiniTestSuite::Unit.new
You can also add an after test callback by updating your test_helper.rb (or spec_helper.rb) like this
# test_helper.rb
class MyTest < Minitest::Unit
after_tests do
# ... after test code
end
end

Ruby Watir can't find the assert method outside of the running class?

I have a class that I want to use in many test cases:
require 'rubygems'
require 'test/unit'
require 'watir'
class Tests < Test::Unit::TestCase
def self.Run(browser)
# make sure Summary of Changes exists
assert( browser.table(:class, "summary_table_class").exists? )
# make sure Snapshot of Change Areas exists
assert( browser.image(:xpath, "//div[#id='report_chart_div']/img").exists? )
# make sure Integrated Changes table exists
assert( browser.table(:id, 'change_table_html').exists? )
end
end
However, when run in one of my test cases:
require 'rubygems'
require 'test/unit'
require 'watir'
require 'configuration'
require 'Tests'
class TwoSCMCrossBranch < Test::Unit::TestCase
def test_two_scm_cross_branch
test_site = Constants.whatsInUrl
puts " Step 1: go to the test site: " + test_site
ie = Watir::IE.start(test_site)
Tests.Run(ie)
end
end
I get the error:
NoMethodError: undefined method `assert' for Tests:Class
C:/p4/dev/webToolKit/test/webapps/WhatsIn/ruby-tests/Tests.rb:8:in `Run'
What's missing? Thanks!
assert() is an instance method on TestCase so would only be available to instances of Tests. You are calling it inside a class method so Ruby is looking for a class method in Tests which doesn't exist.
A better way to do this is to make Tests a module and the Run method an instance method:
module Tests
def Run(browser)
...
end
end
Then include the Tests module in your test class:
class TwoSCMCrossBranch < Test::Unit::TestCase
include Tests
def test_two_scm_cross_branch
test_site = Constants.whatsInUrl
puts " Step 1: go to the test site: " + test_site
ie = Watir::IE.start(test_site)
Run(ie)
end
end
which will make the Run method available to the test and Run() will find the assert() method in the test class.
It might be worth a try to remove the asserts all together, and just use .exists?.

Weird error when trying to test method with argument in Mocha. Is it a bug or is it me?

It's rather hard to find any documentation on Mocha, so I'm afraid I'm totally at sea here. I have found a problem with stubbing methods that pass arguments. So for instance if I set up a class like this:
class Red
def gets(*args)
#input.gets(*args)
end
def puts(*args)
#output.puts(*args)
end
def initialize
#input = $stdin
#output = $stdout
end
private
def first_method
input = gets.chomp
if input == "test"
second_method(input)
end
end
def second_method(value)
puts value
second_method(value)
end
end
Yes it's contrived, but it's a simplification of the idea that you may have a method that you don't want called in the test.
So I might write a test such as:
setup do
#project = Red.new
#project.instance_variable_set(:#input, StringIO.new("test\n"))
#project.stubs(:second_method)
end
should "pass input value to second_method" do
#project.expects(:second_method).with("test").once
#project.instance_eval {first_method}
end
Now I would expect this to pass. But instead I get this rather arcane error message:
Errno::ENOENT: No such file or directory - getcwd
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/backtrace_filter.rb:12:in `expand_path'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/backtrace_filter.rb:12:in `block in filtered'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/backtrace_filter.rb:12:in `reject'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/backtrace_filter.rb:12:in `filtered'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/expectation_error.rb:10:in `initialize'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/mockery.rb:53:in `new'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/mockery.rb:53:in `verify'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/api.rb:156:in `mocha_verify'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/integration/mini_test/version_131_and_above.rb:27:in `run'
This means absolutely nothing to me, other than something deep in Mochas bowels has just gone clang. If I write the same sort of test without an argument passing to the second method I get no problem. Am I missing something?
I think it must be something in shoulda causing the problem. I use test/unit, and everything appears to be OK.
require 'rubygems'
require "test/unit"
require 'mocha'
require File.dirname(__FILE__) + '/../src/red'
class RedTest < Test::Unit::TestCase
def setup
#project = Red.new
#project.instance_variable_set(:#input, StringIO.new("test\n"))
#project.stubs(:second_method)
end
def test_description_of_thing_being_tested
#project.expects(:second_method).with("test").once
#project.instance_eval {first_method}
end
end
gives the following output:
stephen#iolanta:~/tmp/red/test # ruby red_test.rb
Loaded suite red_test
Started
.
Finished in 0.000679 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
stephen#iolanta:~/tmp/red/test #
Sorry - I've only just seen this. It's better to submit bug reports to us in Lighthouse. What documentation have you found? Have you seen the RDoc on Rubyforge? What sort of documentation were you looking for that you did not find?
I've been unable to reproduce your bug. What version of Ruby, Rubygems, Shoulda & Mocha were you using?
You can see the results of me running your test in this Gist.

Resources