Detect that code run by RSpec, Ruby - ruby

I would like to be able to know that my code is run under rspec or not. Is this possible?
The reason is that I am loading some error loggers that would be cluttered with deliberate errors (expect{x}.to raise_error) during testing.
I have looked at my ENV variable, and there is no (apparent) signs of a test environment variable.

Add at the beginning of your spec_helper.rb:
ENV['RACK_ENV'] = 'test'
Now you can check in your code whether the RACK_ENV is test or not.

Related

Testing a Jekyll site with rspec and capybara, getting a bizarre race-case on rspec start

So check this out: it appears as though, upon running bundle exec rspec, there's a race between jekyll serve and puma/rspec's boot up. Sometimes I run the command, and my tests run fine. Other times, I get the error for each of my spec files: cannot load such file -- /path/to/project/sitename_jekyll/_layouts/spec/form_spec.rb which is interesting cause that's not where my spec files are located. They're in /path/to/project/sitename_jekyll/spec/form_spec.rb.
What's crazy is that I can literally just re-run the command over and over and over again and sometimes it'll go through and run the spec tests in the correct location, and sometimes it'll look for them in _layouts and error out. It probably runs correctly maybe once out of ever three or five attempts. All the other times I get the following errors:
Here's what my spec_helper.rb looks like: https://gist.github.com/johnhutch/2cddfafcde0485ff021501d5696c0c2d
And here's an example test file:
https://gist.github.com/johnhutch/a35d15c170f5fd9ca07998bf035d111d
My .rspec only contains two lines:
--color
--require spec_helper
And here's the output, both successful and unsuccesful, back to back:
https://gist.github.com/johnhutch/7927d609170ef5c70a595735502b128d
HEEELLLLLP!
This sounds like jekyll is changing the current directory while building the site, which since it is being run in a thread also affects the tests RSpec is trying to run (See https://bugs.ruby-lang.org/issues/9785 for why Dir.chdir is not threadsafe) - leading to attempts to load things from incorrect locations.
A potential solution to this would be to wait for the Jekyll site to be built before actually running your tests. A comment in your spec_helper seems to state that someone thought passing force_build: true would do this but from a quick perusal of the jekyll-rack code I don't think that's true and you actually need to wait for compiling? to return false (v 0.5) (complete? to return true in the current master branch) to ensure building has finished (as well as passing force_build). This could either be done in a loop sleeping and checking (simpler)
sleep 0.1 while <jekyll app>.compiling?
or (if using the master branch) via the mutex/conditional Rack::Jekyll exposes like in its test suite - https://github.com/adaoraul/rack-jekyll/blob/master/test/helper.rb#L49
Note: Also check my comment about your tests that aren't actually testing anything.
As per Thomas Walpole's super helpful responses this ended up working:
sleep 0.1 while Capybara.app.compiling?
inserted right after:
51 Capybara.app = Rack::Jekyll.new(force_build: true)
in my spec_helper.rb
Thanks again, Thomas!

How do I get the whole error backtrace for rspec?

Currently I am using
parallel tests
rspec
allure 0.8.0
After I run the tests I get the following error:
RSpec::Core::MultipleExceptionError
I need the whole backtrace of the error. Is it some parameter that I need to pass to the command that I use to run and is there a permanent way so that it always prints the whole error
You have two options, to define a helper with that attribute or in command line
## spec/spec_helper.rb
config.full_backtrace = true # false to dismiss
or in the command line
$ rspec spec/folder/your_spec.rb --backtrace
If you get this error you need to find the specific test in the rspec log (scroll up). There you will see details about the issue.
In my case error was shown only on CI and I needed some time in order to figure out that everything I need is "hidden" somewhere in the super long output.

Testing Ruby-based CLIs with Aruba and Bundler

I have an RSpec suite, run via Bundler, that is testing a number of different command-line applications using Aruba. It works fine ... as long as the command being tested is not itself written in Ruby using Bundler. But I cannot figure out how to prevent the RSpec suite's bundler config from interfering with the execution of commands that themselves use Bundler - at least, not without extreme measures.
I have tried various permutations of unset_bundler_env_vars and with_clean_env, to no avail. Here's an example of a technique I thought would work:
describe 'my ruby app' do
before :each { unset_bundler_env_vars }
it 'should work' do
Bundler.with_clean_env { run_simple ruby_command_name }
end
end
I also tried unset_bundler_env_vars without with_clean_env, and vice-versa, in case they interfered with each other. No dice.
The only way I've gotten it to work is to massage Aruba's copy of the environment manually, like this:
before :all do
aruba.environment.tap do |env|
if env.include? 'BUNDLE_ORIG_PATH' then
env['PATH'] = env['BUNDLE_ORIG_PATH']
%w(BUNDLE_BIN_PATH BUNDLE_GEMFILE BUNDLE_ORIG_PATH GEM_HOME RBENV_DIR
RBENV_HOOK_PATH RUBYLIB RUBYOPT).each do |key|
env.delete key
end
end
end
end
There must be a better way. Neither the test suite nor the command being tested should know or care what language the other is written in. And my test code that uses Aruba and Bundler should not need to know the details of how bundle exec affects the process environment.
So what am I doing wrong? How should I be doing this?
It looks like unset_bundler_env_vars is deprecated and replaced by delete_by_environment_variable which requires a string param (source).
You might try before :each { delete_environment_variable('BUNDLE_GEMFILE') } in your spec. If that does not work, you may need to iterate through the PATH variable list to delete each one.
In the deprecation notice, there is a work-around, though I am not sure how brittle that would be moving forward.
unset_bundler_env_vars
aruba.environment.clear.update(ENV)
Hope this helps.

Using minitest-reporters with TeamCity (minitest-reporters tries to override reporter selection)

Does anyone have experience with using minitest-reporters for correct test output on TeamCity?
I want to use the JUnit reporter and when I am running the rake test task on my computer, the output is correctly in xml format under test/reports. But when I run the tests from TeamCity, then it tries to use the RubyMine reporter instead (although test_helper specifies JUnit reporter). I found that reports.rb file from mintiest-reporters has these lines:
def self.choose_reporters(console_reporters, env)
if env["TM_PID"]
[RubyMateReporter.new]
elsif env["RM_INFO"] || env["TEAMCITY_VERSION"]
[RubyMineReporter.new]
else
Array(console_reporters)
end
end
And it seems that here the RubiMine reporter is chosen by default when these environment variables are given, although I specified another reprter in my test_helper (and it seems that I could not use the RubiMine reporter as it tries to require some TeamCity files and they somehow cannot be accessed).
I tried to override TEAMCITY_VERSION while calling rake test but it does not seem to work.
Has anyone solved this problem? Or is there some other reporter gem that would work better (I also tried ci_reporter, as it works with my other projects with lower ruby versions but somehow not with 2.0.0)?
I had this exact problem today and was able to solve it by unsetting the 3 environment variables before executing the rake task.
For example, I have a TeamCity command line build step that basically looks like this:
#! /bin/bash
unset TM_PID
unset RM_INFO
unset TEAMCITY_VERSION
rake my_task

What is the best way to write specs for code that depends on environment variables?

I am testing some code that pulls its configuration from environment variables (set by Heroku config vars in production, for local development I use foreman).
What's the best way to test this kind of code with RSpec?
I came up with this:
before :each do
ENV.stub(:[]).with("AWS_ACCESS_KEY_ID").and_return("asdf")
ENV.stub(:[]).with("AWS_SECRET_ACCESS_KEY").and_return("secret")
end
If you don't need to test different values of the environment variables, I guess you could set them in spec_helper instead.
You also can stub the constant:
stub_const('ENV', {'AWS_ACCESS_KEY_ID' => 'asdf'})
Or, if you still want the rest of the ENV:
stub_const('ENV', ENV.to_hash.merge('AWS_ACCESS_KEY_ID' => 'asdf'))
That would work.
Another way would be to put a layer of indirection between your code and the environment variables, like some sort of configuration object that's easy to mock.
This syntax works for me:
module SetEnvVariable
def set_env_var(name, value)
# Old Syntax
# ENV.stub(:[])
# ENV.stub(:[]).with(name).and_return(value)
allow(ENV).to receive(:[]) # stub a default value first if message might be received with other args as well.
allow(ENV).to receive(:[]).with(name).and_return(value)
end
end
As Heroku suggests, you can use Foreman's .env file to store environment variables for development.
If you do that, you can use foreman run to run your specs:
foreman run bundle exec rspec spec
If you're using dotenv to setup your environment during tests but need to modify an env variable for a specific test then following approach can be useful.
A simpler method than stubbing ENV is to replace the environment for the duration of the test, and then restore it afterwards like so:
with_environment("FOO" => "baz") do
puts ENV.fetch("FOO")
end
Using a helper like this:
module EnvironmentHelper
def with_environment(replacement_env)
original_env = ENV.to_hash
ENV.update(replacement_env)
yield
ensure
ENV.replace(original_env)
end
end
By using ensure the original environment is restored even if the test fails.
There's a handy comparison of methods for setting & modifying environment variables during tests including stubbing the ENV, replacing values before / after the test, and gems like ClimateControl.
I'd avoid ENV.stub(:[]) - it does not work if other things are using ENV such as pry(you'll get an error about needing to stub DISABLE_PRY).
#stub_const works well as already pointed out.
You can use https://github.com/littleowllabs/stub_env to achieve this. It allows you to stub individual environment variables without stubbing all of them as your solution suggested.
Install the gem then write
before :each do
stub_env('AWS_ACCESS_KEY_ID', 'asdf')
stub_env('AWS_SECRET_ACCESS_KEY','secret')
end
What you want is the dotenv gem.
Running tests under foreman, as #ciastek suggests, works great when running specs from CLI. But that doesn't help me run specs with Ruby Test in Sublime Text 2. Dotenv does exactly what you, transparently.

Resources