How can I change ruby log level in unit tests based on context - ruby

I'm new to ruby so forgive me if this is simple or I get some terminology wrong.
I've got a bunch of unit tests (actually they're integration tests for another project, but they use ruby test/unit) and they all include from a module that sets up an instance variable for the log object. When I run the individual tests I'd like log.level to be debug, but when I run a suite I'd like log.level to be error. Is it possible to do this with the approach I'm taking, or does the code need to be restructured?
Here's a small example of what I have so far.
The logging module:
#!/usr/bin/env ruby
require 'logger'
module MyLog
def setup
#log = Logger.new(STDOUT)
#log.level = Logger::DEBUG
end
end
A test:
#!/usr/bin/env ruby
require 'test/unit'
require 'mylog'
class Test1 < Test::Unit::TestCase
include MyLog
def test_something
#log.info("About to test something")
# Test goes here
#log.info("Done testing something")
end
end
A test suite made up of all the tests in its directory:
#!/usr/bin/env ruby
Dir.foreach(".") do |path|
if /it-.*\.rb/.match(File.basename(path))
require path
end
end

As you noted you're going to need to set something in the suite.
Something like the following can be used in the setup method. Just call MyLog.setInSuite in your suite and it'll set the level to INFO on setup.
module MyLog
##use_info = false
def MyLog.setInSuite()
##use_info = true
end
def setup
#log = Logger.new(STDOUT)
if (##use_info)
#log.level = Logger::INFO
else
#log.level = Logger::DEBUG
end
end
end

Hmm. I think the way to do it is to change the logging module to test an environment variable, say TEST_SUITE and set the logging level to info if it is set, and debug if it is not.
Then update your test suite to set the TEST_SUITE environment variable at the top, and unset it at the bottom.
Seems a bit clunky, so I'd be interested to see if anyone else has any other ideas.

Related

rspec setup method in separate file

I am trying to create a setup file for rspec so that my '_spec.rb' test only have 'it' blocks like so:
setup.rb
require 'rspec'
module Setup
describe "setup" do
before(:each) do
#foo = 'hello'
'do something'
end
after(:each) do
'do something'
end
end
spec/test_spec.rb
require_relative '../setup.rb'
include Setup
it "test_sample" do
puts #foo
'do test'
end
This returns undefined variable. Can someone shine light onto my issue? I have multiple tests that I want to share the same setup for, and I don't want to repeat the setup for each test.
Use rspec's configure method for this:
# in spec_helper.rb (or setup.rb)
RSpec.configure do |config|
config.before(:each) do
...
end
config.after(:each) do
...
end
end
See here for more info on setting the configuration.

How to define a simple global variable in an rspec test that can be accesed by helper functions

I cant figure out how to use a simple global variable in an rspec test. It seems like such a trivial feature but after much goggleing I havent been able to find a solution.
I want a variable that can be accessed/changed throughout the main spec file and from functions in helper spec files.
Here is what I have so far:
require_relative 'spec_helper.rb'
require_relative 'helpers.rb'
let(:concept0) { '' }
describe 'ICE Testing' do
describe 'step1' do
it "Populates suggestions correctly" do
concept0 = "tg"
selectConcept() #in helper file. Sets concept0 to "First Concept"
puts concept0 #echos tg?? Should echo "First Concept"
end
end
.
#helpers.rb
def selectConcept
concept0 = "First Concept"
end
Can someone point out what I am missing or if using "let" is totally the wrong method?
Consider using a global before hook with an instance variable: http://www.rubydoc.info/github/rspec/rspec-core/RSpec/Core/Configuration
In your spec_helper.rb file:
RSpec.configure do |config|
config.before(:example) { #concept0 = 'value' }
end
Then #concept0 will be set in your examples (my_example_spec.rb):
RSpec.describe MyExample do
it { expect(#concept0).to eql('value') } # This code will pass
end
It turns out the easiest way is to use a $ sign to indicate a global variable.
See Preserve variable in cucumber?
This is an old thread, but i had this question today. I just needed to define a long string to stub out a command that is in multiple files as:
# in each spec file that needed it
let(:date_check) do
<<~PWSH.strip
# lots of powershell code
PWSH
end
# in any context in that file (or a shared context)
before(:each) do
stub_command(date_check).and_return(false)
end
Searched, Stack Overflow, etc, landed on this: Note the usage of the variable doesn't change at all! (Assumes all specs require 'spec_helper')
# in spec_helper.rb
def date_check
<<~PWSH.strip
# lots of powershell code
PWSH
end
# in any context in any spec file
before(:each) do
stub_command(date_check).and_return(false)
end
I suggest you define the variable in the helper file, where it can be used by other helper code, and can be accessed from your tests.
For my project, I wanted to keep all the setup stuff in spec_helper.rb, and use those settings, plus any custom variables and methods in the tests. The following, modified from the RSpec-core 3.10 docs, is not Rails-specific.
Create a new setting for RSpec.configure called my_variable, and give it a value, like this:
# spec/spec_helper.rb
RSpec.configure do |config|
config.add_setting :my_variable
config.my_variable = "Value of my_variable"
end
Access settings as a new read-only property in RSpec.configuration from your test:
# spec/my_spec.rb
RSpec.describe(MyModule) do
it "creates an instance of something" do
my_instance = MyModule::MyClass.new(RSpec.configuration.my_variable)
end
end

Capturing failure details to file using ruby test/unit

I'm writing a quick test using the test/unit gem, and want to write error/failure details to a file. In the teardown section I'm using the #test_passed variable to know when there's a failure, and then I write to a file, but I can't seem to find the proper variable to dump out the method that failed or any failure details.
I really only want to capture the errors. It seems like it should be fairly simple. Anyone know what variables test/unit is using to store the error details?
Below is an example how I'm trying to dump out the errors:
require "test/unit"
class MyTest < Test::Unit::TestCase
def setup
end
def teardown
if #test_passed then
puts "no errors"
else
File.open("errors.txt", "a+") do |f|
f.puts "Error in #{what_is_the_variable_for_the_method_name}"
f.puts "#{variable_with_error_details_like_expecting_this_but_got_that}"
end
end
end
def test_fail
a = 9
assert_equal(a, 10)
end
end
The source files of the Test::Unit framework are installed on your system. Look for testcase.rb.
The results are stored in a #_result member.

Load a Ruby TestCase Without Running It

I'm trying to write a custom tool that runs ruby unit tests with my customizations.
What I need it to do is to load a certain TestCase from given file(through require or whatever), and then run it after doing some calculations and initializations.
Problem is, the moment I require "test/unit" and a test case, it runs immediately.
What can I do with this?
Thanks.
Since you're running 1.9 and test/unit in 1.9 is merely a wrapper for MiniTest, the following approach should work:
implement your own custom Runner
set MiniTest's runner to your custom runner
Something like (shameless plug from EndOfLine Custom Test Runner, adjusted to Ruby 1.9):
fastfailrunner.rb:
require 'test/unit'
class FastFailRunner19 < MiniTest::Unit
def _run args = []
puts "fast fail runner"
end
end
~
example_test.rb:
require 'test/unit'
class ExampleTest < Test::Unit::TestCase
def test_assert_equal
assert_equal 1, 1
end
def test_lies
assert false
end
def test_exceptions
raise Exception, 'Beware the Jubjub bird, and shun the frumious Bandersnatch!'
end
def test_truth
assert true
end
end
run.rb:
require_relative 'fast_fail_runner'
require_relative 'example_test'
MiniTest::Unit.runner= FastFailRunner19.new
If you run this with
ruby run.rb
the custom FastFailRunner19 will be used, which does nothing.
What about reading file content as a regular text file and doing eval on its content after you initialize/calculate things you say? It may not be sufficient for your needs and may require manual setup and execution of testing framework.
Like that (I put heredoc instead of reading file). Basically content is just a string containing your test case code.
content = <<TEST_CASE
class YourTestCase
def hello
puts 'Hello from eval'
end
end
YourTestCase.new.hello
TEST_CASE
eval content
Note: Altough I'd rather not use eval if there is another way. One should be extra careful when evaling code from string manually in any language.
You could collect the test cases you want to deferred its executions and store them in an array. Afterwards you would create a block execution code. For instance:
test_files = ['test/unit/first_test.rb'] #=> Testcases you want to run
test_block = Proc.new {spec_files.each {|f|load f} } #=> block storing the actual execution of those tests.
Once you're ready to call those testcases you just do test_block.call.
To generalize a bit, when thinking about deferring or delaying code executions, closures are a very elegant and flexible alternative.

Rspec, mapping spec files to ruby files under test

What I want is a way of not having to 'require' the class under test in each spec file.
So hoping there is a means of setting the root of the source code under test and rspec automatically mapping my tests, or any other means of automatically mapping specs to ruby files.
In Rspec for rails this happens magically, but this is not a rails project and I can't find any useful information.
I am assuming you have a lib folder and a spec folder within your project where you have code and specs respectively. Create a spec/spec_helper.rb and add
# project_name/spec/spec_helper.rb
$: << File.join(File.dirname(__FILE__), "/../lib")
require 'spec'
require 'main_file_within_lib_folder_that_requires_other_files'
Now within your individual spec files now you just need to add the following line like rails
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
What you have to do is to redefine Object.const_missing.
Found this basic example, modify it to fit your needs (set the right path, etc.):
def Object.const_missing(name)
#looked_for ||= {}
str_name = name.to_s
raise "Class not found: #{name}" if #looked_for[str_name]
#looked_for[str_name] = 1
file = str_name.downcase
require file
klass = const_get(name)
return klass if klass
raise "Class not found: #{name}"
end

Resources