I have a class with 20 rspec tests, and the result should be the exact same whether my main logic goes through a feature flag or not.
I am wondering what the best way to test this suite so that on Jenkins it can test both directions?
I want to avoid having to copy paste the 20 tests again, just to add another line like:
featureFlag.enabled = True
Right before the test logic.
Is this possible? Thanks
It depends which test framework you use? RSpec? Minitest?
In RSpec you could for instance use a shared example
RSpec.shared_examples "some example" do |flag|
before do
featureFlag.enabled = flag
end
it "works with feature flag '#{flag}'" do
expect(something).to be true
end
end
RSpec.describe SomeClass do
include_examples "some example", true
include_examples "some example", false
end
In Minitest you can just use a module.
https://relishapp.com/rspec/rspec-core/docs/example-groups/shared-examples
Related
And it does not work. Can you help me how to write the right test for its case? Thanks very much.
#model.rb
def driver_iq_api
if Rails.env.production?
'https://admin.sss/xmlpost.cfm'
else
'https://eeem/ws/xmlpost.cfm'
end
end
model_spec.rb
describe 'private methods' do
context '.driver_iq_api' do
it 'production true' do
allow(Rails.env).to receive(:production?) {true}.and_return('https://admin.sss/xmlpost.cfm')
end
it 'production false' do
allow(Rails.env).to receive(:production?) {false}.and_return('https://eeem/ws/xmlpost.cfm')
end
end
end
Setting Rails.env to something other than test, inside a test, is a bad idea. Whilst you may "get away with it" in this case, it could cause all sorts of weird side-effects in general, such as writing data to a non-test database.
In addition, it seems you're writing unit tests for private methods, which is typically a bad idea. You should only normally test the public interface of a class.
As stated above, this sort of config should ideally live in a configuration file, such as e.g. application.yml.
The other answer already shows how you could stub the behaviour, but as yet another alternative, you could consider injecting the environment as a method dependency:
def driver_iq_api(env: Rails.env)
if env.production?
'https://admin.sss/xmlpost.cfm'
else
'https://eeem/ws/xmlpost.cfm'
end
end
describe '#driver_iq_api' do
it 'production env' do
expect(model.driver_iq_api(env: 'production'.inquiry)).to eq 'https://admin.sss/xmlpost.cfm'
end
it 'test env' do
expect(model.driver_iq_api(env: 'test'.inquiry)).to eq 'https://eeem/ws/xmlpost.cfm'
end
end
Note that for example, 'test'.inquiry returns a ActiveSupport::StringInquirer instance - which is the same behaviour as calling Rails.env.
...But to reiterate my original point, I wouldn't bother testing this method at all.
As for you test, I'm not familiar with the receive...{block} syntax you use, and I doubt seriously that tests what you think it is testing.
Here is a test suite that tests much more succinctly, and is super easy for anyone to read:
describe '.driver_iq_api' do
subject { MyModel.driver_iq_api }
context 'when in production' do
allow(Rails.env).to receive(:production?).and_return(true)
it { is_expected.to eq 'https://admin.sss/xmlpost.cfm' }
end
context 'when not in production' do
it { is_expected.to eq 'https://eeem/ws/xmlpost.cfm' }
end
end
And to reinforce what #Tom Lord said, this approach is dangerous for methods that actually do things like write to databases and such. I'd use this only for the type of method you have in your example...returning a resource name, boolean, etc. based on env. If your platform-sensitive code is buried deep in a method and can't be tested in isolation, then refactor it out of the big method into an atomic method that can easily be tested (and mocked!).
I agree you should pull this out into config since it is static, but to answer your question:
it "when production" do
allow(Rails.env).to receive(:production?).and_return(true)
expect(my_class.send(:driver_iq_api)).to eq('https://admin.sss/xmlpost.cfm')
end
Hi i am working with a RoR project with ruby-2.5.0 and rails 5. I am using AWS SQS. I have created a job as follows:-
class ReceiptsProcessingJob < ActiveJob::Base
queue_as 'abc'
def perform(receipt_id)
StoreParserInteractor.process_reciept(receipt_id)
end
end
Now i want to write unit test for it. I tried like:-
# frozen_string_literal: true
require 'rails_helper'
describe ReceiptsProcessingJob do
describe "#perform_later" do
it "scan a receipt" do
ActiveJob::Base.queue_adapter = :test
expect {
ReceiptsProcessingJob.perform_later(1)
}.to have_enqueued_job
end
end
end
But it doesnot cover StoreParserInteractor.process_reciept(receipt_id). Please help how can i cover this. Thanks in advance.
The example is testing the job class. You need to write a spec for StoreParserInteractor and test the method process_reciept.
Something along the lines of (pseudo code):
describe StoreParserInteractor do
describe "#process_receipt" do
it "does that" do
result = StoreParserInteractor.process_receipt(your_data_here)
expect(result to be something)...
end
end
end
But, the Rails guide suggests this kind of test:
assert_enqueued_with(job: ReceiptsProcessingJob) do
StoreParserInteractor.process_reciept(receipt_id)
end
Maybe this increases code coverage as well.
In my opinion, you shouldn't actually test the ActiveJob itself, but the logic behind it.
You should write a test for StoreParserInteractor#process_reciept. Think of ActiveJob as an "external framework" and it is not your responsibility to test the internals of it (e.g. if the job was enqueued or not).
As kitschmaster said, don't test ActiveJob classes, in short
I am new to rspec and we are using it for test automation for our application. I have three hooks that are reused in every rspec test. The hooks call a number modules that launch are used to launch & log into the site plus some other usable methods.
I have created a hooks.rb file and placed those hooks in there and call it within the rspec test but now I have lost the ability to call the instance variables that relate to the methods I need. I could globalize the variables but I have read its not a good idea to do so.
Does anyone have any insight on what the best approach would be?
I am including code example.
before(:all) do
< this before hook include making files available and initialization browser >
** very section
end
after(:all) do
#client.quit
end
before(:each) do
#page.goto
end
** I have to include this in every spec file and would not like to have to. One call to the location so it is included for any spec.
Thank you,
Joe
So, my spec_helper.rb consist of the following information:
RSpec.configure do |config|
config.before(:all) do
require_relative '../../lib/env'
filedir = File.expand_path('../../etc',File.dirname(__FILE__))
config = Matr::Configuration.new("#{filedir}/config.yaml", ENV["M_ENV"] || "development")
#client = config.construct_selenium_driver "Insight: Login"
#client.window.resize_to(1280,720)
#page = Matr::Pages::Insight::LoginPage.new(#client)
#account_page = Matr::Pages::Insight::InsightPage.new(#client)
#choose_account = Matr::Pages::Insight::ChooseAccount.new(#client)
#logout = Matr::Pages::Insight::LogOut.new(#client)
#nav_bar = Matr::Pages::Insight::NavBar.new(#client)
#db = Matr::Models::DB.new
#manage_campaigns = Matr::Pages::Insight::ManageCampaignsPage.new(#client)
end
end
You can define hooks for all examples when you configure rspec in your spec_helper.rb
For example you could do
RSpec.configure do |config|
config.before(:all) do
...
end
end
If you don't actually need this for all specs then you can use rspec's metadata filters. For example, to apply to all of the request specs you'd do
config.before(:all, type: :request) do
...
end
To only run against the specs with type :request. You can also make up your own metadata keys, so if you did
config.before(:all, browser: true) do
...
end
Then that before(:all) would run for example groups created in this way:
describe "something", browser: true do
...
end
Have you considered using shared examples? Generally, you should not have to roll your own approach to creating reusable modules.
I have a test that looks like this:
class PageTest < ActiveSupport::TestCase
describe "test" do
test "should not save without attributes" do
page = Page.new
assert !page.save
end
end
end
When running the tests, I get 0 tests, 0 assertions. If I remove the describe "test" do, I get the 1 test, 1 assertions. So I have the feeling that the describe "..." do is actually making the test disappear.
What is going on here? What am I missing?
Looks like you're mixing up minitest specs and ActiveSupport::TestCase. If you check the rails guides on testing the test method is explained but it's not used with describe.
Rails adds a test method that takes a test name and a block. It
generates a normal MiniTest::Unit test with method names prefixed with
test_. So,
test "the truth" do
assert true
end
acts as if you had written
def test_the_truth
assert true
end
The describe syntax is explained in the minitest docs under the spec section and is used with it (and not test). Like so:
describe "when asked about cheeseburgers" do
it "must respond positively" do
#meme.i_can_has_cheezburger?.must_equal "OHAI!"
end
end
I'm writing integration tests using Rspec and Capybara. I've noticed that quite often I have to execute the same bits of code when it comes to testing the creation of activerecord options.
For instance:
it "should create a new instance" do
# I create an instance here
end
it "should do something based on a new instance" do
# I create an instance here
# I click into the record and add a sub record, or something else
end
The problem seems to be that ActiveRecord objects aren't persisted across tests, however Capybara by default maintains the same session in a spec (weirdness).
I could mock these records, but since this is an integration test and some of these records are pretty complicated (they have image attachments and whatnot) it's much simpler to use Capybara and fill out the user-facing forms.
I've tried defining a function that creates a new record, but that doesn't feel right for some reason. What's the best practice for this?
There are a couple different ways to go here. First of all, in both cases, you can group your example blocks under either a describe or context block, like this:
describe "your instance" do
it "..." do
# do stuff here
end
it "..." do
# do other stuff here
end
end
Then, within the describe or context block, you can set up state that can be used in all the examples, like this:
describe "your instance" do
# run before each example block under the describe block
before(:each) do
# I create an instance here
end
it "creates a new instance" do
# do stuff here
end
it "do something based on a new instance" do
# do other stuff here
end
end
As an alternative to the before(:each) block, you can also use let helper, which I find a little more readable. You can see more about it here.
The very best practice for your requirements is to use Factory Girl for creating records from a blueprint which define common attributes and database_cleaner to clean database across different tests/specs.
And never keep state (such as created records) across different specs, it will lead to dependent specs. You could spot this kind of dependencies using the --order rand option of rspec. If your specs fails randomly you have this kind of issue.
Given the title (...reusing code in Rspec) I suggest the reading of RSpec custom matchers in the "Ruby on Rails Tutorial".
Michael Hartl suggests two solutions to duplication in specs:
Define helper methods for common operations (e.g. log in a user)
Define custom matchers
Use these stuff help decoupling the tests from the implementation.
In addition to these I suggest (as Fabio said) to use FactoryGirl.
You could check my sample rails project. You could find there: https://github.com/lucassus/locomotive
how to use factory_girl
some examples of custom matchers and macros (in spec/support)
how to use shared_examples
and finally how to use very nice shoulda-macros
I would use a combination of factory_girl and Rspec's let method:
describe User do
let(:user) { create :user } # 'create' is a factory_girl method, that will save a new user in the test database
it "should be able to run" do
user.run.should be_true
end
it "should not be able to walk" do
user.walk.should be_false
end
end
# spec/factories/users.rb
FactoryGirl.define do
factory :user do
email { Faker::Internet.email }
username { Faker::Internet.user_name }
end
end
This allows you to do great stuff like this:
describe User do
let(:user) { create :user, attributes }
let(:attributes) { Hash.new }
it "should be able to run" do
user.run.should be_true
end
it "should not be able to walk" do
user.walk.should be_false
end
context "when user is admin" do
let(:attributes) { { admin: true } }
it "should be able to walk" do
user.walk.should be_true
end
end
end