OS specific tests with ruby - ruby

I've the following Rakefile
require 'bundler/gem_tasks'
require 'rake/testtask'
Rake::TestTask.new do |task|
task.libs << %w(test lib)
task.pattern = 'test/**/*_test.rb'
end
task default: :test
And test are defined with minitest:
require 'test_helper'
require 'certmanager'
class CertificateTest < Minitest::Test
context 'settings' do
should 'retrieve settings' do
assert_equal 'test123', Certmanager::Settings.key_passphrase
end
end
end
Now there is also some OS specific code like FileUtils.ln_s on Linux and FileUtils.cp on Windows. This code requires different tests. In this case e.g. assert_equal filepath, File.readlink(linkpath) (Linux) vs. assert File.exist?(filepath) (windows)
What is the best practice to differentiate between OS Types?
Do I have t write the tests in a way that Linux can test windows specific code and vice versa?
Is it possible to differentiate "inline" in an test?
Is it necessary to have two different testsets and decide in the Rakefile which set needs to be executed? Is it even possible int the Rakefile?

In order to be able to run every test regardless of the current OS, i wrote a module TestHelper. This module contains severall methods to
pretend a windows or posix environment for the test
setup stubs for methods that are not implemented (or work in a different way) on the actual OS - currently I've only found those methods on windows
check if the actual OS is windows or not (if it is necessary to know)
In the test it is possible to simply use TestHelper.setup_windows_env or TestHelper.setup_posix_env whenever it has to be OS specific.
require 'rbconfig'
require 'fileutils'
module TestHelper
extend self
def setup_windows_env
is_windows?
RbConfig::CONFIG.stubs(:[]).with('host_os').returns('windows')
ENV.stubs(:[]).with('OS').returns('Windows_NT')
end
def setup_stubs_for_windows_in_posix_env
class << FileUtils
def ln_s(src, dest, options = {})
File.open(dest, 'w') { |file| file.write src }
return 0
end
end
class << File
def readlink(file_name)
return File.read(file_name)
end
end
end
def setup_posix_env
setup_stubs_for_windows_in_posix_env if is_windows?
RbConfig::CONFIG.stubs(:[]).with('host_os').returns('darwin')
ENV.stubs(:[]).with('OS').returns('OSX')
end
def is_windows?
#is_windows ||= ApplicationToTest::Util::OS.is_windows?
end
end
For completeness ApplicationToTest::Util::OS.is_windows?:
require 'rbconfig'
module ApplicationToTest
module Util
module OS
def self.is_windows?
begin
env_os = ENV['OS']
rescue NameError
return false
end
if RbConfig::CONFIG['host_os'] =~ /^mingw2$|^mingw$|^mswin$|^windows$/
true
elsif env_os == 'Windows_NT'
true
else
false
end
end
end
end
end

Related

Capybara.page not in scope after extending capybara-screenshot's after_failed_example method

I'm trying to override the after_failed_example method so I can inflict some custom file naming on our screenshots. I'm loading the module as an initializer.
So far, so good, but the Capybara.page.current_url is blank, making me think I need to require something additional?
require "capybara-screenshot/rspec"
module Capybara
module Screenshot
module RSpec
class << self
attr_accessor :use_description_as_filename
attr_accessor :save_html_file
end
self.use_description_as_filename = true
self.save_html_file = true
def self.after_failed_example(example)
if example.example_group.include?(Capybara::DSL) # Capybara DSL method has been included for a feature we can snapshot
Capybara.using_session(Capybara::Screenshot.final_session_name) do
puts ">>>> Capybara.page.current_url: " + Capybara.page.current_url.to_s
if Capybara::Screenshot.autosave_on_failure && failed?(example) && Capybara.page.current_url != ''
saver = Capybara::Screenshot.new_saver(Capybara, Capybara.page, Capybara::Screenshot.save_html_file?, set_saver_filename_prefix(example))
saver.save
example.metadata[:screenshot] = {}
example.metadata[:screenshot][:html] = saver.html_path if saver.html_saved?
example.metadata[:screenshot][:image] = saver.screenshot_path if saver.screenshot_saved?
end
end
end
private
def self.set_saver_filename_prefix(example)
return example.description.to_s.gsub(" ", "-") if Capybara::Screenshot.use_description_as_filename?
return Capybara::Screenshot.filename_prefix_for(:rspec, example)
end
end
end
end
end
This is successfully overriding the capybara-screenshot/rspec method, and any of the Capybara::Screenshot static information is accessible, but not Capybara session related information (afa I can tell).
For example, Capybara.page.current_url.to_s is null when overridden, but present when not.
I was missing a require (kind of silly mistake):
require 'capybara/rspec'

Ruby tests don't run

I'm trying Ruby and Unit Testing but my simple attempts at running test cases are returning nothing. The curious thing is that the tests were running before and returning the number of tests that were ran and the number of assertions and so on. But for some reason they stopped running. I have two separated files:
#TipoMovimento.rb
class TipoMovimento
attr_accessor :designacao
attr_accessor :cor
attr_accessor :regras
def initialize(aDesignacao, aCor, asRegras)
#designacao = aDesignacao
#cor = aCor
#regras = asRegras
end
def ==(other)
other.class == self.class && other.state == self.state
end
def state
self.instance_variables.map { |variable| self.instance_variable_get variable }
end
end
and
#TesteTipoMovimento.rb
require './TipoMovimento.rb'
require 'test/unit'
class TesteTipoMovimento < Test::Unit::TestCase
def setup
#tm = TipoMovimento.new('Des1', 'Cor1', ['r1', 'r2'])
end
def tc_equal
tm2 = TipoMovimento.new('Des1', 'Cor1', ['r1', 'r2'])
assert_true(tm2 == #tm)
tm2 = TipoMovimento.new('Des2', 'Cor1', ['r1', 'r2'])
assert_false(tm2 == #tm)
end
end
Both files are in the same folder. Unfortunately, when I run the test file, nothing happens. After I press enter the prompt simply ignores my command. Something like:
C:\My Ruby Files\>ruby TesteTipoMovimento.rb
C:\My Ruby Files\>
This is obviously something simple that I'm missing, so if anyone could help me I would appreciate it. Thanks!
You have no tests in that test class. To make method a test, prefix its name with test_.
class TesteTipoMovimento < Test::Unit::TestCase
def setup
#tm = TipoMovimento.new('Des1', 'Cor1', ['r1', 'r2'])
end
def test_tc_equal
tm2 = TipoMovimento.new('Des1', 'Cor1', ['r1', 'r2'])
assert_true(tm2 == #tm)
tm2 = TipoMovimento.new('Des2', 'Cor1', ['r1', 'r2'])
assert_false(tm2 == #tm)
end
end

Override rake test:units runner

I recently decided to write a simple test runtime profiler for our Rails 3.0 app's test suite. It's a very simple (read: hacky) script that adds each test's time to a global, and then outputs the result at the end of the run:
require 'test/unit/ui/console/testrunner'
module ProfilingHelper
def self.included mod
$test_times ||= []
mod.class_eval do
setup :setup_profiling
def setup_profiling
#test_start_time = Time.now
end
teardown :teardown_profiling
def teardown_profiling
#test_took_time = Time.now - #test_start_time
$test_times << [name, #test_took_time]
end
end
end
end
class ProfilingRunner < Test::Unit::UI::Console::TestRunner
def finished(elapsed_time)
super
tests = $test_times.sort{|x,y| y[1] <=> x[1]}.first(100)
output("Top 100 slowest tests:")
tests.each do |t|
output("#{t[1].round(2)}s: \t #{t[0]}")
end
end
end
Test::Unit::AutoRunner::RUNNERS[:profiling] = proc do |r|
ProfilingRunner
end
This allows me to run the suites like so rake test:xxx TESTOPTS="--runner=profiling" and get a list of Top 100 tests appended to the end of the default runner's output. It works great for test:functionals and test:integration, and even for test:units TEST='test/unit/an_example_test.rb'. But if I do not specify a test for test:units, the TESTOPTS appears to be ignored.
In classic SO style, I found the answer after articulating clearly to myself, so here it is:
When run without TEST=/test/unit/blah_test.rb, test:units TESTOPTS= needs a -- before its contents. So the solution in its entirety is simply:
rake test:units TESTOPTS='-- --runner=profiling'

Creating and removing folders in Tests

I am trying to test a scenario where my app writes some files to a folder and that folder should then be removed after the test.
class TestDirectory < Test::Unit::TestCase
def setup
#target = "some/path"
FileUtils.mkdir_p(#target)
end
def teardown
FileUtils.rm_rf(#target)
end
test "remove directory" do
#some tests
end
end
But for some reason the folder does not get removed. I have also tried to set the secure option to false for FileUtils#rm_rf but that didn't help either. Neither the parent nor the target folder are world writable, which could be an issue according to the documentation. I am running on Mac OS X Lion.
What am I doing wrong?
This code works for me:
require 'test/unit'
require 'fileutils'
class TestDirectory < Test::Unit::TestCase
def setup
#target = "/tmp/tmpfolder"
FileUtils.mkdir_p(#target)
end
def teardown
FileUtils.rm_rf(#target)
end
def test_something
assert 1 + 1 == 2
end
end

With Test::Unit, how can I run a bit of code before all tests (but not each test)?

In my test app, which uses test::unit, I need to start by pulling a bunch of data from various sources. I'd like to only do this once - the data is only read, not written, and doesn't change between tests, and the loading (and error checking for the loading), takes some time.
There are values that I DO want reset every time, and those are easy enough, but what if I want persistant accessible values? What's the best way to do this?
I'm especially interested in solutions that would let my push those assignments to some module that can be included in all my tests, since they all need access to this data.
Why do you need it inside the test? You could define it gloabl:
gem 'test-unit'#, '>= 2.1.1' #startup
require 'test/unit'
GLOBAL_DATA = 11
class My_Tests < Test::Unit::TestCase
def test_1()
puts "Testing startup 1"
assert_equal(11, GLOBAL_DATA)
end
end
GLOBAL_DATA could be a (singleton)-class (respective an instance).
If you have only one testclass, you may use TestCase.startup:
gem 'test-unit'#, '>= 2.1.1' #startup
require 'test/unit'
class My_Tests < Test::Unit::TestCase
def self.startup
puts "Define global_data "
##global_data = 11
end
def test_1()
puts "Testing 1"
assert_equal(11, ##global_data = 11)
end
def test_2()
puts "Testing 2"
assert_equal(11, ##global_data = 11)
end
end
You can just put them at the top of the class. They will get executed, and then your tests will get executed.
You could do this in the setup method:
def setup
if !defined?(##initial_data)
# Whatever you need to do to get your initial data
##initial_data = foo
end
#other_data = bar
end

Resources