Jasmine: Testing AJAX calls that manipulate the DOM
Here is a Javascript function reloadUsers()
that fetches a HTML snippet from the server using AJAX and replaces the current .users
container in the DOM:
window.reloadUsers = ->
$.get('/users').then (html) ->
$('.users').html(html)
Testing this simple function poses a number of challenges:
- It only works if there is a
<div class="users">...</div>
container in the current DOM. Obviously the Jasmine spec runner has no such container. - The code requests
/users
and we want to prevent network interaction in our uni...
Mailcatcher: An alternative to inaction_mailer
Looks simpler than inaction_mailer:
gem install mailcatcher
mailcatcher
Setup Rails to send mails to 127.0.0.1:1025. Usually you want the following config in config/environments/development.rb
and maybe in test.rb
or cucumber.rb
.
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
:address => 'localhost',
:port => 1025
}
Now you can see sent mails in your browser when opening http://127.0.0.1:1080
Note: In order to s...
makandra/gemika: Helpers for testing Ruby gems
We have released a new library Gemika to help test a gem against multiple versions of Ruby, gem dependencies and database types.
Here's what Gemika can give your test's development setup (all features are opt-in):
- Test one codebase against multiple sets of gem dependency sets (e.g. Rails 4.2, Rails 5.0).
- Test one codebase against multiple Ruby versions (e.g. Ruby 2.1.8, Ruby 2.3.1).
- Test one codebase against multiple database types (currently MySQL or PostgreSQL).
- Compute a matrix of all possib...
esbuild: Make your Rails application show build errors
Building application assets with esbuild is the new way to do it, and it's great, especially in combination with Sprockets (or Propshaft on Rails 7).
You might be missing some convenience features, though.
Here we cover one specific issue:
Once you have started your development Rails server and esbuild with the --watch
option (if you used jsbundling-rails to set up, you probably use bin/dev
), esbuild will recompile your assets upon change, but build errors will only be printed to the terminal. Your application won't complain about them ...
Rspec 3: what to do when `describe` is undefined
When tests might not run with skipping RSpec
in the RSpec.describe
failing with the error undefined method 'describe' for main:Object
this card will help you out!
In RSpec 3 the DSL like describe
is exposed globally by default. Therefore it is not necessary to write Rspec.describe
.
However, there is a config option to disable this beavior, which also disables the old should
-syntax:
RSpec.configure do |config|
config.disable_monkey_patching!...
Postgres in Alpine docker container: sorting order might differ
In CI test runs I noticed that string sorting order changed after switching from a debian-based PostgreSQL docker image to one that is based on Alpine Linux.
Debian image sorting: bar Bar foo Foo
Alpine image sorting: Bar Foo bar foo
Explanation
Alpine Linux is a very slim linux distribution that results in small docker image sizes (roughly 100MB instead of 150MB), so it's a popular choice. However, it does not have all comman locales installed and does not use all locales that a user installs by default.
Postgres orders string co...
git: find the version of a gem that releases a certain commit
Sometimes I ran across a GitHub merge request of a gem where it was not completely obvious in which version the change was released. This might be the case for a bugfix PR that you want to add to your project.
Git can help you to find the next git tag that was set in the branch. This usually has the name of the version in it (as the rake release
task automatically creates a git tag during release).
git name-rev --tags <commit ref>
Note
The more commonly used
git describe
command will return the last tag before a c...
Testing focus/blur events with Cucumber
This is a problem when using Selenium with Firefox. We recommend using ChromeDriver for your Selenium tests.
This setup allows to test focus/blur events in Cucumber tests (using Selenium). For background information, see How to solve Selenium focus issues.
Cucumber step definition:
# This step is needed because in Selenium tests, blur events are not triggered
# when the browser has no focus.
When /^I unfocus the "(.*?)" field to trigger ja...
Making ZSH the default shell on Ubuntu 20.04
ZSH is an alternative command line shell that includes some features like spelling correction, cd automation, better theme, and plugin support. You can replace Bash with ZSH like following:
sudo apt-get install zsh
Setting ZSH as default login shell
sudo usermod -s /usr/bin/zsh $(whoami)
Opening a new terminal window will show you a dialog where you can configure your initial ZSH config (Option 2 recommended).
Afterwards you can install the plugin manager Oh-My-ZSH and select a prop...
How to migrate CoffeeScript files from Sprockets to Webpack(er)
If you migrate a Rails application from Sprockets to Webpack(er), you can either transpile your CoffeeScript files to JavaScript or integrate a CoffeeScript compiler to your new process. This checklist can be used to achieve the latter.
- If you need to continue exposing your CoffeeScript classes to the global namespace, define them on
window
directly:
-class @User
+class window.User
- Replace Sprocket's
require
statement with Webpacker's...
Good real world example for form models / presenters in Rails
We have often felt the pain where our models need to serve too many masters. E.g. we are adding a lot of logic and callbacks for a particular form screen, but then the model becomes a pain in tests, where all those callbacks just get in the way. Or we have different forms for the same model but they need to behave very differently (e.g. admin user form vs. public sign up form).
There are many approaches that promise help. They have many names: DCI, presenters, exhibits, form models, view models, etc.
Unfortunately most of these approaches ...
Upgrading Capybara with deprecated Integer selectors
Capybara added a deprecation warning in version 3.35.3 (version from 2019) that shows up if your selector is not of type String or Symbol.
Example:
click_link(10) # bad
click_link("10") # good
You might encounter this error e.g. in a pagination step or similar where you want to click on numbers. To figure out where this deprecation warning comes from try to run the tests with a step output.
bundle exec parallel_cucumber --test-options "--format=pretty" feature
The deprecation message looks like following:
Locator In...
Custom RSpec matcher for allowed values (or assignable_values)
In contrast to RSpec's included allow_value
matcher, the attached matcher will also work on associations, which makes it ideal for testing assignable_values
.
Usage example
describe Unit do
describe '#building' do
it 'should only allow buildings that a user has access to' do
building = build(:building)
other_building = build(:building)
unauthorized_building = build(:building)
power = Power.new(build(:user))
Power.with_power(power) do
expect(power).to receive(:buildings).at_least...
Spreewald: Content-Disposition not set when testing a download's filename
Precondition
- You are not using javascript tests
- The file is served from a public folder (not via controller)
Problem description
If you deliver files from a public folder it might be that the Content-Disposition
header is not set. That's why the following spreewald step might raise an error:
Then I should get a download with filename "..."
expected: /filename="some.pdf"$/
got: nil (using =~) (RSpec::Expectations::ExpectationNotMetError)
Solution
One solution...
Geordi 1.3 released
Changes:
- Geordi is now (partially) tested with Cucumber. Yay!
- geordi cucumber supports a new @solo tag. Scenarios tagged with
@solo
will be excluded from parallel runs, and run sequentially in a second run - Support for Capistrano 2 AND 3 (will deploy without
:migrations
on Capistrano 3) - Now requires a
.firefox-version
file to set up a test firefox. By default now uses the system Firefox/a test Chrome/whatever and doesn't print warnings any more. -
geordi deploy --no-migrations
(aliased-M
): Deploy with `cap ...
Debug Ruby code
This is an awesome gadget in your toolbox, even if your test coverage is great.
-
gem install ruby-debug
(Ruby 1.8) orgem install debugger
(Ruby 1.9) - Start your server with
script/server --debugger
- Set a breakpoint by invoking
debugger
anywhere in your code - Open your application in the browser and run the code path that crosses the breakpoint
- Once you reach the breakpoint, the page loading will seem to "hang".
- Switch to the shell you started the server with. That shell will be running an irb session where you can step thr...
Interacting with a Microsoft Exchange server from Ruby
Microsoft Exchange service administrators can enable Exchange Web Services (EWS) which is a rather accessible XML API for interacting with Exchange. This allows you to read and send e-mails, create appointments, invite meeting attendees, track responses, manage to-do tasks, check user availability and all other sorts of things that are usually only accessible from Outlook.
You can implement an EWS by hand-rolling your XML (the [docs](http://msdn.microsoft.com/en-us/...
Webmock normalizes arrays in urls
Typhoeus has a different way of representing array params in a get
request than RestClient.
Typhoeus: http://example.com/?foo[0]=1&foo[1]=2&foo[2]=3
RestClient: http://example.com/?foo[]=1&foo[]=2&foo[]=3
Webmock normalizes this url when matching to your stubs, so it is always http://example.com/?foo[]=1&foo[]=2&foo[]=3
. This might lead to green tests, but in fact crashes in real world. Rack::Utils.build_nested_query
might help to build a get re...
Protip: Clone large projects multiple times
Large projects usually have large test suites that can run for a long time.
This can be annoying as running tests blocks you from picking up the next story -- but it doesn't have to be that way!
Simply clone your project's repo twice (or even more often).
When your work on a feature branch is done, simply push that branch and check it out on your 2nd copy to run tests there.
You can pick up a new story and work on that on your "main" project directory.
If you do it right, you will even be able to run tests in both your 2nd copy and your m...
How to use Simplecov to find untested code in a Rails project with RSpec and Cucumber
Simplecov is a code coverage tool. This helps you to find out which parts of your application are not tested.
Integrating this in a rails project with rspec, cucumber and parallel_tests is easy.
-
Add it to your Gemfile and bundle
group :test do gem 'simplecov', require: false end
-
Add a
.simplecov
file in your project root:SimpleCov.start 'rails' do # any custom configs like groups and filters can be here at a central place enable_cov...
Rails: How to check if a certain validation failed
If validations failed for a record, and you want to find out if a specific validation failed, you can leverage ActiveModel's error objects.
You rarely need this in application code (you usually just want to print error messages), but it can be useful when writing tests.
As an example, consider the following model which uses two validations on the email
attribute.
class User < ApplicationRecord
validates :email, presence: true, uniqueness: true
end
Accessing errors
Let's assume we have a blank user:
user = Us...
Mysql::Error: SAVEPOINT active_record_1 does not exist: ROLLBACK TO SAVEPOINT active_record_1 (ActiveRecord::StatementInvalid)
Possible Reason 1: parallel_tests - running more processes than features
If you run old versions of parallel_tests with more processes than you have Cucumber features, you will get errors like this in unexpected places:
This is a bug caused by multiple processes running the same features on the same database.
The bug is fixed in versions 0.6.18+.
Possib...
How to bind an event listener only once with Unpoly
You can use Unpoly's up.on with a named listener function and immediately unbind this event listener with { once: true }
:
up.on('up:fragment:inserted', { once: true }, function () { ... })
In Unpoly 1 you can immediately unregister the listener with up.off:
up.on('up:fragment:inserted', function fragmentInsertedCallback() {
up.off('up:fragment:inserted', fragmentInsertedCallback)
// ... code for the callback function, which should run only once
})
Exam...
Case Study: Analyzing Web Font Performance
Table of contents of the linked article:
What are Web Fonts?
- Advantages of Web Fonts
- Disadvantages of Web Fonts
- Fallback Fonts
- CSS3 @font Declaration Example
- Fallback Font Example
- Render Blocking and Critical Rendering Path
- FOIT
Optimizing Web Font Delivery Further
- Prioritize Based On Browser Support
- Choose Only Styles You Need
- Character Sets
- Host Fonts Locally or Prefetch
- Store in LocalStorage with Base64 Encoding
- Another Method