Email spec doesn't match body content in Rails - ruby-on-rails

I'm using email_spec gem to test a simple email, but for some reason the body content appears to be empty:
1) ContactMailer welcome email to new user renders the body
Failure/Error: mail.should have_body_text("Hi")
expected the body to contain "Hi" but was ""
# ./spec/mailers/contact_mailer_spec.rb:17:in `block (3 levels) in <top (required)>'
Every other example passes. The template file is called welcome_email.text.erb. Not sure why body is not matched, but the email does have a body when it gets sent.
Edit: the Rspec code is:
let(:mail) { ContactMailer.welcome_email(email) }
it "renders the body" do
mail.should have_body_text("Hi")
end

The best way I've found to do this is:
it "contains a greeting" do
mail.html_part.body.should match /Hi/
end
You can also use text_part in place of html_part if you want to check the plain text part of a multipart message.
Also note that others may recommend using #encoded, but I had trouble using that with long URLs, as they may get line-wrapped during the encoding process.

So, I was experiencing the same thing. I was trying to test my mailers without loading all of Rails.
What finally solved my problem was adding this to my test:
(note that my test is in test/unit/mailers/my_mailer_test.rb - you may have to adjust paths)
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.view_paths = File.expand_path('../../../../app/views', __FILE__)
Basically, without the view paths pointing to your views directory, the template is not found and all the parts (html, text, etc) are blank.
NOTE: The directory specified is NOT the one the actual templates are in. The mailer knows to look for a directory in the template root named after the class itself.
Here's a sample in minitest/spec
require 'minitest/spec'
require 'minitest/autorun'
require "minitest-matchers"
require 'action_mailer'
require "email_spec"
# NECESSARY TO RECOGNIZE HAML TEMPLATES
unless Object.const_defined? 'Rails'
require 'active_support/string_inquirer'
class Rails
def self.env
ActiveSupport::StringInquirer.new(ENV['RAILS_ENV'] || 'test')
end
end
require 'haml/util'
require "haml/template"
end
# END HAML SUPPORT STUFF
require File.expand_path('../../../../app/mailers/my_mailer', __FILE__)
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.view_paths = File.expand_path('../../../../app/views', __FILE__)
describe MyMailer do
include EmailSpec::Helpers
include EmailSpec::Matchers
let(:the_email){ MyMailer.some_mail() }
it "has the right bit of text" do
the_email.must have_body_text("some bit of text")
end
end

Related

In a Rails engine Is it possible for Rspec to make use of Rspec support system helpers from another engine?

Given a Rails engine_one that has a spec support file engine_one/spec/support/system/order_functions.rb, containing functionality to support the testing of various order system tests such as simulating a logged in user, adding products to an order etc and contains methods such as log_visitor_in that get used extensively when testing order processing etc...
So now in engine_two that extends some ordering functionality from engine_one I wish to add a new system test that first has to log a visitor in. So how can I make use of that support method from from engine_one?
So far I have mounted the engines in the dummy app
I have required engine_one in engine_two/lib/engine.rb
I have required the support file in the relevant test but it can't be found and obviously I have added engine_one to engine_two.gemspec
engine_two/spec/rails_helper.rb
require 'engine_one' # and any other gems you need
engine_two/lib/engine_two/engine.rb
require 'engine_one'
in the relevant system test I have the following
engine_two/spec/system/new_payment_methods_spec.rb
require 'rails_helper'
include EngineOne::System
RSpec.describe "order_payment_feature", type: :system do
before do
driven_by(:rack_test)
end
it "has order payment options" do
log_visitor_in
end
end
This results in the following error
Failure/Error: include EngineOne::System
NameError:
uninitialized constant EngineOne::System
Did you mean? SystemExit
And the helper
module System
def log_visitor_in()
administrator = create(:visitor)
visit ccs_cms.login_url
fill_in 'login_name', with: visitor.login_name
fill_in 'Password', with: visitor.password
click_button 'Login'
end
end
I have tried with a require instead of an include but that results in a file not found error
Plus I have tried changing the include path to
include EngineOne::Spec::Support::System resulting in the same error
So I guess I'm looking for the correct path but I am stuck or missing some other way to include the helper.
These are Rails 7 engines.
When you require a file, ruby searches for it relative to paths in $LOAD_PATH; spec/ or test/ are not part of it.
app directory is a special one in rails, any subdirectory automatically becomes part of autoload_paths. Auto load paths can be seen here ActiveSupport::Dependencies.autoload_paths.
Any classes/modules defined inside app/* directories can be used without requiring corresponding files. Rails v7 uses zeitwerk to automatically load/reload files by relying on the 'file name' to 'constant name' relationship. That's why folders map to namespaces and files map to classes/modules.
To fix your issue put any shared code where it can be grabbed with require. Type $LOAD_PATH in the console:
>> $LOAD_PATH
=>
["/home/alex/code/stackoverflow/lib",
"/home/alex/code/stackoverflow/vendor",
"/home/alex/code/stackoverflow/app/channels",
"/home/alex/code/stackoverflow/app/controllers",
"/home/alex/code/stackoverflow/app/controllers/concerns",
"/home/alex/code/stackoverflow/app/helpers",
"/home/alex/code/stackoverflow/app/jobs",
"/home/alex/code/stackoverflow/app/mailers",
"/home/alex/code/stackoverflow/app/models",
"/home/alex/code/stackoverflow/app/models/concerns",
"/home/alex/code/stackoverflow/engines/question/lib", # <= engine's lib looks good
"/home/alex/code/stackoverflow/engines/question/app/components",
"/home/alex/code/stackoverflow/engines/question/app/controllers",
"/home/alex/code/stackoverflow/engines/question/app/controllers/concerns",
...
Put shared files in engines's lib directory. Since we're outside of app directory, rails is not the boss anymore, any path and filename combination will work.
# question/lib/testing_support/blah.rb # <= note the filename
module System
def log_visitor_in
administrator = create(:visitor)
visit ccs_cms.login_url
fill_in 'login_name', with: visitor.login_name
fill_in 'Password', with: visitor.password
click_button 'Login'
end
end
Now that file can be required
# test/test_helper.rb or spec/rails_helper.rb
# after environment and rails requires
require "testing_support/blah" # => loads System module
# ...
That's it, use it in your spec
require 'rails_helper'
RSpec.describe "order_payment_feature", type: :system do
include System # include is for modules; now we have its functions in this spec
before { log_visitor_in }
it 'should accept this answer' do
visit 'questions/71362333'
expect(page).to have_content('accepted')
end
end
Additionally you can require your files any way you wish with an absolute path, regardless of $LOAD_PATH.
require EngineOne::Engine.root + 'spec/support/system/order_functions.rb'
# or something else
Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each { |f| require f }

ActionMailer fails when testing mailer

Rails 4.1.4 and Rspec 3
I'm doing a VERY basic email test with Rspec. If I call the mailer from rails console, it works perfectly. If I call it from the mailer spec I get:
wrong number of arguments (0 for 1..2)
The mailer is very basic:
def create_team_invite(org, email)
#organization = org
mail(:to=>email, :subject=>'Test Subject')
end
The Test is pretty basic too:
it 'can send out emails to invite others to create teams' do
UserMailer.create_team_invite(#org, 'test#test.com').deliver
expect(ActionMailer::Base.deliveries.count).to eq 1
mail = ActionMailer::Base.deliveries.first
expect(mail.subject).to eq 'Test Subject'
expect(mail.from).to eq 'test#test.com'
end
Its failing in the "mail(:to..." line in the mailer. Seems like maybe its some configuration issue in my environment, but I have Test setup exactly the same as Dev, using SMTP and sending it to a Mailcatcher port. I caught the exception and looked at the Backtrace, but don't see anything unusual...
Anyone seen this before?
Update: providing additional info that was requested.
My test.rb, minus comments:
Rails.application.configure do
config.cache_classes = true
config.eager_load = false
config.serve_static_assets = true
config.static_cache_control = 'public, max-age=3600'
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
config.action_dispatch.show_exceptions = false
config.action_controller.allow_forgery_protection = false
config.action_mailer.delivery_method = :smtp
config.action_mailer.perform_deliveries = true
config.active_support.deprecation = :stderr
config.action_mailer.default_url_options = { :host => 'lvh.me:3000', :only_path=>false }
end
The entire rspec failure is:
UserMailer
Team Joining Email
can send out emails to invite others to create teams (FAILED - 1)
Failures:
1) UserMailer Team Joining Email can send out emails to invite others to create teams
Failure/Error: UserMailer.create_team_invite(#team, 'test#test.com').deliver
ArgumentError:
wrong number of arguments (0 for 1..2)
# ./app/mailers/user_mailer.rb:11:in `create_team_invite'
# ./spec/mailers/user_mailer_spec.rb:36:in `block (3 levels) in <top (required)>'
Finished in 0.25858 seconds (files took 29 minutes 48 seconds to load)
1 example, 1 failure
The way I configure my email is via an initializer that loads an email.yml file from my config, per environment. Exact same process used by both test and dev, with the same settings. (Again, I'm sending to Mailcatcher, instead of just to mail_delivery :test)
Update 2
I have narrowed it down to the Mailer missing the "request" object. If I dig through where the error is occurring (AbstractController rendering.rb, line 109) it tries to reference the request object:
if defined?(request) && request && request.variant.present?
This is calling over to Rack test.rb line 121:
def request(uri, env = {}, &block)
env = env_for(uri, env)
process_request(uri, env, &block)
end
So its like the Rack Test.rb class is being seen as the request method in that abstractcontroller... but I dont know how, or why, or why this is happening in this particular project...
I get this same exact error, and it seems that it's trying to call rack-test request code, when I'm just trying to test a mailer object. So what I did is just not include all the stuff that I put in the spec_helper for the mailer spec. That fixed the problem for me.
# require 'spec_helper' -> I removed this line,
# and manually require the files that's needed to just test the mailer object:
require 'rubygems'
require './config/environment'
require './app/mailers/your_mailer'
Note: I'm just doing a rack project using action_mailer, no rails stuff. So your solution will be different, but you get the idea.
Update:
After doing some more troubleshooting. I found this problem in my spec_helper.rb file
RSpec.configure do |config|
config.include include RackSpecHelper # notice the double include
...
end
# where RackSpecHelper is a custom module
module RackSpecHelper
include Rack::Test::Methods
# a bunch of other helper methods
end
Notice the double include on this line
config.include include RackSpecHelper
At first I tried just removing the line and my mailer test is running just fine. Suspicious with the double include, I remove the include so it's just like this
config.include RackSpecHelper
Now my mailer test runs just fine without having to do manual require like I posted earlier above (and it can run together with the other test that uses rack test stuff).
The double include is basically doing
config.include(include(RackSpecHelper))
which include the RackSpecHelper in the configure block, which loads all the methods in the top level namespace! (very bad thing). It works, but that means all the methods from Rack::Test::Methods are in the global namespace.
So I'm guessing in your case, you might have a line in the spec_helper that include Rack::Test::Methods in the global namespace like this
include Rack::Test::Methods
remove it and instead put it in the RSpec config like this
RSpec.configure do |config|
config.include RackSpecHelper
end

Rspec not loading support files

I have this file:
# support/auth_macros.rb
module AuthMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
#logged_in_user = FactoryGirl.create(:user, username: "logged_in")
sign_in #logged_in_user
end
end
def logout_user
before(:each) do
sign_out #logged_in_user
end
end
end
In my spec_helper file, I have this line:
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
Yet when I run rspec, I get errors like:
undefined local variable or method `login_user' for RSpec::ExampleGroups::PostsController::POSTCreate::WhenSignedIn:Class
The relevant function is located in support/auth_macros, which I assume would be picked up by the require statement in my spec_helper.
Any idea what might be going on?
You have required the file, but the method is wrapped inside a module. You need to either remove the wrapping module or include it within your group test.
Update:
To be 100% specific: require loads the file and do nothing else. After file is required, the module has been created, but it is not included. You need to include it with: include AuthMacros
If your file relative path like support/auth_macros.rb and you wanna load it on selenium_helper.rb file, you need to do both step bellow:
Call require to load the file: require 'support/auth_macros'
And include it using: include AuthMacros
Hope it help.

Why Won't RSpec Pass This Test? [duplicate]

My java web application is running on tomcat at http://localhost:8080/
Writing my first spec, home_spec:
require 'spec_helper'
describe "home" do
it "should render the home page" do
visit "/"
page.should have_content("hello world")
end
end
And running:
rspec
I get:
F
Failures:
1) home should render the home page
Failure/Error: visit "/"
NoMethodError:
undefined method `visit' for #<RSpec::Core::ExampleGroup::Nested_1:0x242870b7>
# ./spec/home/home_spec.rb:7:in `(root)'
Finished in 0.012 seconds
1 example, 1 failure
Failed examples:
rspec ./spec/home/home_spec.rb:6 # home should render the home page
Shouldn't this work because I have included capybara in the spec_helper?
How will it know to visit the correct url? what if my url is localhost:3030 or localhost:8080?
My gemfile:
source 'http://rubygems.org'
gem "activerecord"
gem "rspec"
gem "capybara"
gem "activerecord-jdbcmysql-adapter"
My spec_helper:
require 'capybara/rspec'
Regarding to rspec issues (https://github.com/rspec/rspec-rails/issues/360)
you should put
config.include Capybara::DSL
in spec_helper.rb, inside the config block.
The default directory that Capybara::RSpec now looks at to include the Capybara::DSL and Capybara::RSpecMatchers is changed from requests to features.
After I renamed my requests directory to features I got the matcher and DSL methods available again without having to explicitly include them.
See the following commit
Also make sure your tests are in the /spec/features directory. According to rspec-rails and capybara 2.0, Capybara v2 and higher will not be available by default in RSpec request specs. They suggest to "...move any tests that use capybara from spec/requests to spec/features."
By default the capybara DSL is included automatically if the file is in spec/requests, spec/integration or if the example group has :type => :request.
Because your file is in spec/home the capybara helpers aren't being included. You can either conform to one of the patterns above or adding include Capybara::DSL should also do the trick (you might also need to replicate some of the before(:each) stuff that would be setup.)
First check it out
If you are not success,
Add this code your end of the your spec helper actually out of the RSpec.configure block as well
module ::RSpec::Core
class ExampleGroup
include Capybara::DSL
include Capybara::RSpecMatchers
end
end
1) Add to ‘rails_helper’ config:
config.include Capybara::DSL
config.include Capybara::RSpecMatchers
And comment out the `require 'spec_helper'` line.
2) Add to 'spec_helper':
require 'rails_helper'

undefined method `find_link' for #<Cucumber::Rails::World:0x818e02e8> (NoMethodError)

Rspec obviously hates me. I kinda hate him back.
#features/step_definitions/custom_steps.rb
Then /^I should see the link "([^\"]*)"$/ do |linked_text|
find_link(linked_text)
end
#link.feature
Then I should see the link "foo"
From terminal:
undefined method `find_link' for #<Cucumber::Rails::World:0x818e02e8> (NoMethodError)
./features/step_definitions/custom_steps.rb:115:in `/^I should see the link "([^\"]*)"$/'
My env.rb file:
#features/support/env.rb
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
# It is recommended to regenerate this file in the future when you upgrade to a
# newer version of cucumber-rails. Consider adding your own code to a new file
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
# files.
ENV["RAILS_ENV"] ||= "cucumber"
require File.expand_path(File.dirname(__FILE__) + '/../../config/environment')
require 'cucumber/formatter/unicode' # Remove this line if you don't want Cucumber Unicode support
require 'cucumber/rails/world'
require 'cucumber/rails/active_record'
require 'cucumber/web/tableish'
require 'webrat'
require 'webrat/core/matchers'
require 'spec/stubs/cucumber'
Scenes::load
Webrat.configure do |config|
config.mode = :rails
config.open_error_files = false # Set to true if you want error pages to pop up in the browser
end
# If you set this to false, any error raised from within your app will bubble
# up to your step definition and out to cucumber unless you catch it somewhere
# on the way. You can make Rails rescue errors and render error pages on a
# per-scenario basis by tagging a scenario or feature with the #allow-rescue tag.
#
# If you set this to true, Rails will rescue all errors and render error
# pages, more or less in the same way your application would behave in the
# default production environment. It's not recommended to do this for all
# of your scenarios, as this makes it hard to discover errors in your application.
ActionController::Base.allow_rescue = false
# If you set this to true, each scenario will run in a database transaction.
# You can still turn off transactions on a per-scenario basis, simply tagging
# a feature or scenario with the #no-txn tag. If you are using Capybara,
# tagging with #culerity or #javascript will also turn transactions off.
#
# If you set this to false, transactions will be off for all scenarios,
# regardless of whether you use #no-txn or not.
#
# Beware that turning transactions off will leave data in your database
# after each scenario, which can lead to hard-to-debug failures in
# subsequent scenarios. If you do this, we recommend you create a Before
# block that will explicitly put your database in a known state.
Cucumber::Rails::World.use_transactional_fixtures = true
# How to clean your database when transactions are turned off. See
# http://github.com/bmabey/database_cleaner for more info.
if defined?(ActiveRecord::Base)
begin
require 'database_cleaner'
DatabaseCleaner.strategy = :truncation
rescue LoadError => ignore_if_database_cleaner_not_present
end
end
What is wrong? Thank you.
This error is telling you none of your steps or helpers define this method.
Are you trying to use one of the helpers buried in Webrat? It sounds like you want:
Webrat::Locators.find_link

Resources