File.read empty for a non empty file when testing with rspec - ruby

New to rubby and rspec i am trying to test a class that opens and write to a file.
The class name is SimpleLogger
Here is the spec that generates an error:
describe SimpleLogger do
...
context 'when using a file' do
require 'fakefs/spec_helpers'
before(:all) do
#path = 'my_file'
logger = SimpleLogger.new #path
logger.write "Hello, world!"
logger.close
end
...
it 'we expect the file to have a valid content' do
expect(File.read(#path)).to eq "Hello, world!\n"
end
end
end
The error generated is:
Failure/Error: expect(File.read(#path)).to eq "Hello, world!\n"
expected: "Hello, world!\n"
got: ""
(compared using ==)
Diff:
## -1,2 +1 ##
-Hello, world!
The file exists on my file system, and when I'm testing a simple puts Find.read("my_file") on an independant ruby file i've got the expected result.
I've tested and have the same issue without the fakefs gem
Why is it when run in a spec it doesn't work?
And beside that i fail to understand the advantage of fakefs, as it creates the file juste the same. So why fakefs is used?
And as it creates the file should i erase it within the spec?
Thanks in advance ;)

From the documentation - it seems that you need to include the helpers to activate the FakeFS:
FakeFS::SpecHelpers provides a simple macro for RSpec example groups to turn FakeFS on and off.
To use it simply require 'fakefs/spec_helpers', then include FakeFS::SpecHelpers into any
example groups that you wish to use FakeFS in. For example:
require 'fakefs/spec_helpers'
describe "Some specs that deal with files" do
include FakeFS::SpecHelpers
...
end
By default, including FakeFS::SpecHelpers will run for each example inside a describe block.
If you want to turn on FakeFS one time only for all your examples, you will need to
include FakeFS::SpecHelpers::All.
Alternatively, you can include FakeFS::SpecHelpers in all your example groups using RSpec's
configuration block in your spec helper:
require 'fakefs/spec_helpers'
Spec::Runner.configure do |config|
config.include FakeFS::SpecHelpers
end
If you do the above then use_fakefs will be available in all of your example groups.
You will also need to use before(:each) instead of before(:all) - like many unit test helpers, FakeFS adheres to unit-test isolation principles, in which side-effects of one test should not affect another's. That is why after every test, the gem 'resets' the state of its container, and clears all files from it.

Related

How to fetch RSpec test name in before(:each) block without a spec_helper

So here's an example basic test suite:
describe "Main test suite" do
it "should run test #1" do
...
end
it "should run test #2" do
...
end
end
I want to add a before(:each) which does some special logic with the full test name (it'll be inserting the test name as a metadata header into all of the HTTP requests made by each test). I've found that using "#{self.class.description}" only captures the test suite name ("Main test suite" in this case), but I also need to capture the test name itself.
I've seen some other similar questions on StackOverflow such as Getting the full RSpec test name from within a before(:each) block, but the answers all involve adding Spec::Runner.configure or RSpec.configure options to the spec_helper.rb, but we're running these tests through a custom environment which doesn't use a spec_helper.rb, so I need a solution that doesn't depend on that.
I've also seen other examples such as How to get current context name of rspec where they are doing the logging in the test itself instead of in a before(:each) block, so they can just do something like: it "should Bar" do |example| puts "#{self.class.description} #{example.description}" end. But we have hundreds of these tests and I don't want to copy-paste the same logic in every test -- this seems like an ideal use case for a before(:each) block.
describe "Main test suite" do
before(:each) do |x|
puts "#{x.class.description} - #{x.example.description}"
end
it "should run test #1" do
...
end
it "should run test #2" do
...
end
end
I got it working by putting this at the top of my file:
RSpec.configure do |config|
config.before(:each) do |x|
do_stuff("#{x.class.description} - #{x.example.description}")
end
end

Show the complete description of an example

In RSpec, how can we generate a string from an example, representing the current meta description of the current example?
Example:
RSpec.describe Api::V1::Public::Signin::ByEmailController, type: :controller do
describe 'POST #create' do
context 'when the provider is "email"' do
context 'when there is a saved email' do
context 'when the password is good' do
it 'signs in' do
expect(current_metadata_description).to eq 'Api::V1::Public::Signin::ByEmailController POST #create when the provider is "email" when there is a saved email when the password is good signs in'
end
end
end
end
end
end
As we can see, the current meta description of the given example is:
Api::V1::Public::Signin::ByEmailController POST #create when the provider is "email" when there is a saved email when the password is good signs in
Thanks.
Edit:
The current_metadata_description is not provided by RSpec. I just invented it to give an example of what I would like to have.
While I am unsure what your overall intentions are for doing this.
Rspec::Core::ExampleGroup.it yields a RSpec::Core::Example to the block. As with all yielding methods one can capture the yielded Object in a block local variable. RSpec::Core::Example has a full_description method which appears to be what you are looking for. RSpec Source.
For Example (no pun intended):
describe String do
context 'Down' do
context 'Deeper' do
it 'can describe itself' do |ex|
# ex is the specific RSpec::Core::Example for this it block
expect(ex.full_description).to eq('String Down Deeper can describe itself')
end
end
end
end
running this will return
.
Finished in 0.003 seconds (files took 0.21998 seconds to load)
1 example, 0 failures
It is imperative that you capture the yielded value if you want the desired output as the block itself is executed in the context of an instance of RSpec::Core::ExampleGroup so calling self.class.metadata[:full_description] will contain the description of the RSpec::Core::ExampleGroup but not the RSpec::Core::Example itself (e.g. "String Down Deeper")
As described in the docs if this is for logging purposes (or something similar where you would like this information for each example) you can access the RSpec::Core::Example in Hooks excerpt:
# Useful for configuring logging and/or taking some action based
# on the state of an example's metadata.
#
# RSpec.configure do |config|
# config.before do |example|
# log example.description
# end
#
# config.after do |example|
# log example.description
# end
#
# config.around do |example|
# log example.description
# example.run
# end
# end
If you really wanted to patch this in (and I am not endorsing it) You could do the following
class RSpec::Core::ExampleGroup
def instance_exec(*args,&blk)
if args.first.is_a?(RSpec::Core::Example)
self.define_singleton_method(:current_metadata_description) do
args.first.full_description
end
end
super(*args,&blk)
end
end
Then your test would be exactly as you stated.
Try to run rspec with --format option:
bundle exec rspec --format documentation
Update:
I don't know any rspec method that would do that for you. One way I can think of is, write the spec description to a file while running the tests and then read the description from it in your custom current_metadata_description method. Here is a tutorial on Generating documentation from specs. Hope that helps!

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

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.

Where to put helper functions for rake tasks and test files in Ruby on Rails?

In my Rails application I have a file sample_data.rb inside /lib/tasks as well as a bunch of test files inside my /spec directory.
All these files often share common functionality such as:
def random_address
[Faker::Address.street_address, Faker::Address.city].join("\n")
end
Where should I put those helper functions? Is there some sort of convention on this?
Thanks for any help!
You could create a static class, with static functions. That would look something like this:
class HelperFunctions
def self.random_address
[Faker::Address.street_address, Faker::Address.city].join("\n")
end
def self.otherFunction
end
end
Then, all you would need to do is:
include your helper class in the file you want to use
execute it like:
HelperFunctions::random_address(anyParametersYouMightHave)
When doing this, make sure you include any dependencies in your HelperFunctions class.
If you're sure it's rake only specific, you also can add in directly in RAILS_ROOT/Rakefile (that's probably not the case for the example you use).
I use this to simplify rake's invoke syntax :
#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require File.expand_path('../config/application', __FILE__)
def invoke( task_name )
Rake::Task[ task_name ].invoke
end
MyApp::Application.load_tasks
That way, I can use invoke "my_namespace:my_task" in rake tasks instead of Rake::Task[ "my_namespace:my_task" ].invoke.
You share methods in a module, and you place such a module inside the lib folder.
Something like lib/fake_data.rb containing
module FakeData
def random_address
[Faker::Address.street_address, Faker::Address.city].join("\n")
end
module_function
end
and inside your rake task just require the module, and call FakeData.random_address.
But, if it is like a seed you need to do every time you run your tests, you should consider adding this to your general before all.
E.g. my spec_helper looks like this:
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
RSpec.configure do |config|
config.use_transactional_fixtures = true
config.infer_base_class_for_anonymous_controllers = false
config.order = "random"
include SetupSupport
config.before(:all) do
load_db_seed
end
end
and the module SetupSupport is defined in spec/support/setup_support.rb and looks as follows:
module SetupSupport
def load_db_seed
load(File.join(Rails.root, 'db', 'seeds.rb'))
end
end
Not sure if you need to load the seeds, or are already doing this, but this is the ideal spot to also generate needed fake data.
Note that my setup support class is defined in spec/support because the code is only relevant to my specs, I have no rake task also needing the same code.

Resources