Disable SimpleCov if you only run a fraction of your tests
Coverage reports are rarely useful if you run only small parts of your test suite.
Just don't not load SimpleCov in this case, and you will see less noise in your test output:
if RSpec.configuration.files_to_run.count > 5
require "simplecov"
SimpleCov.start 'rails'
end
See also
Unicode character class escape: Match text by Unicode properties
The linked MDN article is quite informative of a neat feature supported by all major browsers: Unicode character class escape.
You can use it to write regular expressions that work on the full UTF-8 space, not just Latin/ASCII. For example, a password policy matcher might include regular expressions like [A-z]
or [0-9]
, but those do not match e.g. German umlauts or [Eastern Arabic Numerals](https:/...
ActiveRecord: Named bindings in conditions
In Active Record you can use named bindings in where
-conditions. This helps you to make your code more readable and reduces repetitions in the binding list.
Example without named bindings
User.where(
'name = ? OR email = ?',
params[:query],
params[:query]
)
Example with named bindings
User.where(
'name = :query OR email = :query',
query: params[:query]
)
Capybara will not find links without an href attribute
Capybara will fail to find <a>
tags that are missing an href
attribute. This will probably happen to you every now and then on JavaScript-heavy applications.
An example would be an AngularJS application where the following HTML actually works. [1]
<a ng-click="hello()">Hello</a>
Capybara will fail to find that link, even though looking it up via the DOM shows it:
>> find_link("Hello")
Capybara::ElementNotFound: Unable to find link "Hello"
>> find("a").text
=> "Hello"
To make find_link
and click_link
work, ...
Don't define a controller action called #process
Remember that your controller actions share the same method space with private methods defined in ActionController::Base
. If your controller behaves in super-weird ways, check that you don't overwrite some internal method with a controller action.
Examples for internal methods:
#process
#process_action
#cookies
#params
#request
#response
Debugging
If you accidentally did overwrite some internal method, you may come across an ArgumentError
with the message `wrong number of arguments (given 1, expe...
Updated: Integrating ESLint
Updated the instructions for ESLint to work with version 5.x.
Improve accessibility with [aria-required] in SimpleForm
SimpleForm comes with an option browser_validations
which could be used to give fields that have a presence validation the HTML required attribute. We usually turn it off due to difficulties controlling its behavior and appearance. Instead we only mark required fields with an asterisk (*) next to its label. Blind users probably only discover the validation issue after submitting the form due to the now displayed error messages.
A compromise with better acce...
RSpec: Marking sections in long examples
RSpec examples can get quite long, especially in feature specs. This makes them hard to read & understand. Also, when executing them, it may take seconds to see any progress.
To improve this, I have successfully been using a little "step" helper in my tests. It marks semantic sections, structuring an example while improving documentation. When the test runs, each step prints its message (or a dot, depending on your formatter).
# spec/support/step_helper.rb
module StepHelper
# Use this helper to label groups of related actions in l...
Updated: A simpler default controller implementation
Added:
etag { flash.to_h }
-
etag { I18n.locale }
(could be left out if all URLs contain a locale fragment, but also doesn't hurt and is a good default)
How to silence Puma for your feature tests
When RSpecs runs the first feature spec, you may see log output like this:
Capybara starting Puma...
* Version 6.5.0, codename: Sky's Version
* Min threads: 0, max threads: 4
* Listening on http://127.0.0.1:39949
You can disable this behavior by tweaking Capybara's Puma server in your spec/support/capybara.rb
:
Capybara.server = :puma, { Silent: true }
Note
You don't need to configure this if you're using system tests with modern versions of Rails. They do [exactly the same](https://github.com/rails/rails/blob/ma...
How to use Ubuntu in English, but still show German formats
If you want to have an English Ubuntu UI, but still see dates, money amounts, paper formats, etc. in German formats, you can fine-tune your /etc/default/locale
like this:
LANG="en_US.UTF-8"
LC_CTYPE="de_DE.UTF-8"
LC_NUMERIC="de_DE.UTF-8"
LC_TIME="de_DE.UTF-8"
LC_COLLATE="de_DE.UTF-8"
LC_MONETARY="de_DE.UTF-8"
LC_PAPER="de_DE.UTF-8"
LC_NAME="de_DE.UTF-8"
LC_ADDRESS="de_DE.UTF-8"
LC_TELEPHONE="de_DE.UTF-8"
LC_MEASUREMENT="de_DE.UTF-8"
LC_IDENTIFICATION="de_DE.UTF-8"
Make sure you have both en...
Git: rebase dependent feature branch after squash
This card will show you how to use git rebase --onto
without confusion.
Use case:
You've got two feature branches (one
and two
), where two
depends on one
. Now commits of branch one
have changed after you branched two
from it (i.e. after code review the commits of branch one
are squashed). Thus the commit history of branch one
has changed. Branch two
's history however didn't change.
Solution:
To make the branches share the same commit history again you will have to rebase and replay (attach) the a...
RSpec: Defining helper methods for an example group
You can define methods in any example group using Ruby's def
keyword or define_method
method:
describe "example" do
def sum(a, b)
a + b
end
it "has access to methods defined in its group" do
expect(sum(3, 4)).to be(7)
end
end
The helper method is also available to groups nested within that group. The helper method is not available to parent or sibling groups.
Global helpers
To define helpers for all specs (or all specs of a type), [define it in a module](https://rspec.info/features/3-12/rspec-core/help...
How to define a table name prefix for all Rails models in a given namespace
ActiveRecord computes table names of model classes, and results are usually just like you'd expect.
Adding a prefix for all classes in a namespace can feel a odd or broken, but does not have to be. It's actually very easy when done right.
Summary (tl;dr)
Here's the short version: Define a table_name_prefix
method in the namespace module, and do not define any table_name_prefix
in ActiveRecord classes inside of it. If this sounds familiar, we have [a card about using it already](https://makandracards.com/makandra/47198-rails-namespac...
How to find out what is running on a port on a remote machine
By convention, common protocols use a defined port, like 80 for HTTP or 443 for HTTPS.
You can use nmap
to find out what service is running behind a given port, and most often see some details about it. This can be helpful if servers don't offer the services you expect for some ports. If you'd like to see what ports are listing on your local machine, you might want to use ss
instead of nmap
.
Note that nmap's service discovery may trigger several requests.
Example
When using nmap, adding the -A
switch will ...
RSpec Argument Matchers: Expecting non-primitive objects as method invocation arguments
Expecting a primitive value as an argument to a method invocation is easy:
expect(object).to receive(:foo).with('arg1', 'arg2')
This expectation would be met with this call:
object.foo('arg1', 'arg2')
But what if the argument is expected to be an object with a given set of methods? E.g. this class with #first_name
and #last_name
methods:
class Person
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
attr_reader :first_name, :last_name
end
``...
Updated: How to allow testing beforeunload confirmation dialogs with modern ChromeDrivers
ChromeDriver 135 started following the W3C spec more closely, which broke testing beforeunload confirmations again. To get working confirmations in a testing browser again, unhandled_prompt_behavior
needs to be set to the hash { default: :ignore }
.
Selenium doesn't currently support setting this option to a hash, so a monkey patch is needed.
Updated: Testing Accessibility using Orca
As it's quite hard to use a screen reader when you have never done so before, I added a guide on how to visually review the spoken text instead.
Debugging Orca's Output
We can visually review everything the screen reader says by hacking together a real-time transcript of Orca's speech.
To do so, run orca in a CLI window telling it to write a temporary debug log (without Braille to reduce noise)
orca --disable braille --debug-file=/tmp/orca-log.txt
Tip: The screen reader will still be audible. You can reduce its volume ...
Managing chrome versions for selenium
Currently we often use geordi to run cucumber
and rspec
tests. Geordi takes care of installing a matching chromedriver
for the installed google-chrome
binary. The google-chrome
binary is managed by apt
.
Another approach is to use the Selenium Manager for installing and using the correct browser versions for you. Here is the setup you need for your integration tests:
Capybara.register_driver :chrome do |app|
options = Sele...
Webpack(er): Analyze the size of your JavaScript components
We're always striving towards keeping our website's JavaScript as small as possible.
If you're using webpack(er), you can use the webpack-bundle-analyzer plugin to get a good overview, which of your JavaScript modules take up how much space, and where you can optimize.
To use it, add it via npm or yarn
yarn add webpack-bundle-analyzer
Then add this to your environment.js
:
// Uncomment this code to show statistics of bundle sizes. Generated file will automatically...
Custom error pages in Rails
Static error pages
To add a few basic styles to the default error pages in Rails, just edit the default templates in public
, e.g. public/404.html
.
A limitation to these default templates is that they're just static files. You cannot use Haml, Rails helpers or your application layout here. If you need Rails to render your error pages, you need the approach below.
Dynamic error pages
- Register your own app as the applicatio...
Don't forget: Automatically remove join records on has_many :through associations
Bad
# Given the following models
class Image < ActiveRecord::Base
has_many :album_images
has_many :albums, through: :album_images
end
class Album < ActiveRecord::Base
has_many :album_images
has_many :images, through: :album_images
end
# Join model
class AlbumImage < ActiveRecord::Base
belongs_to :album
belongs_to :image
end
Destroying a record in this setup will only remove the record itself, and leave orphaned join records behind.
image = Image.last
image.destroy # removes only the `image` record,
...
Rails: Handling actions that need to happen after all transactions
In Rails 7.2. the feature ActiveRecord.after_all_transactions_commit
was added, for code that may run either inside or outside a transaction (we have a special card for nested transactions in general) and needs to perform work after the state changes have been properly persisted. e.g. sending an email.
Example
def publish_article(article)
article.update(published: true)
ActiveRecord.after_all_transactions_commit do
PublishNotification...
Rails: Testing exceptions with the rescue_responses setting
In Rails 7.2 the new default for config.action_dispatch.show_exceptions
is rescuable
.
-
:rescuable
: It will show a Rails error page in the response only for rescuable exceptions as
defined byActionDispatch::ExceptionWrapper.rescue_responses
. In the
event of an unexpected internal server error, the exception that caused
the error will still be raised within the test so as to provide a useful
stack trace and a good debugging experience. -
:all
: It wi...