Automated "git bisect" will make your day
So you're hunting down a regression (or just a bug) and want to use git bisect
to find out when it was introduced? Smart kid.
If you have a shell command ready to reveal if your current state is good or bad, you can have git do most of the work for you.
Using git bisect run <your command>
you can tell git that your command will reveal the issue; git on the other hand will use the return value of that call to decide if the state is good or bad.
...
Regular Expressions - Cheat Sheet
You can write regular expressions some different ways, e.g. /regex/
and %r{regex}
. For examples, look here.
Remember that it is always a good idea to match a regex visually first.
Characters
Literal Characters
[ ] \ ^ $ . | ? * + ( )
Character Classes
[ae] matches a and e, e.g. gr[ae]y => grey or gray => but NOT graay or graey
[0-9] ...
Carrierwave: Built-in RSpec matchers
CarrierWave comes with some RSpec matchers which will make testing more comfortable. Let's say you have an Uploader like this:
class MyUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
# Create different versions of your uploaded files:
version :small do
process resize_to_fill: [100, 100]
end
version :medium do
process resize_to_fit: [200, nil]
end
version :large do
process resize_to_limit: [400, 400]
end
end
Imagine you have a class Movie
with an attribute poster
. In ...
Cucumber step to set cookies in your Capybara session
To set a cookie in your test browser for cucumber tests, you need to know which driver you are using. Use the step below according to your driver.
Rack::Test
Given /^I have a "([^\"]+)" cookie set to "([^\"]+)"$/ do |key, value|
headers = {}
Rack::Utils.set_cookie_header!(headers, key, value)
cookie_string = headers['Set-Cookie']
Capybara.current_session.driver.browser.set_cookie(cookie_string)
end
Note that Rack::Utils
is only used to find out the correct cookie header string (you don't want to generate it yours...
Auto-generating plain-text bodies for HTML e-mails in Rails apps
When building an application that sends e-mails to users, you want to avoid those e-mails from being classified as spam. Most obvious scoring issues will not be relevant to you because you are not a spammer.
However, your application must do one thing by itself: When sending HTML e-mails, you should include a plain-text body or tools like SpamAssassin will apply a significant score penalty. Here is how to do that automatically.
- Add
premailer-rails
to yourGemfile
andbundle
. - Done! ...
Before you make a merge request: Checklist for common mistakes
Merge requests are often rejected for similar reasons.
To avoid this, before you send a merge request, please confirm that your code ...
- has been reviewed by yourself beforehand
- fulfills every requirement defined as an acceptance criterion
- does not have any log or debugging statements like
console.log(...)
,byebug
etc. - has green tests
- has tests...
ActiveSupport includes Timecop-like helpers
ActiveSupport (since 4.1) includes test helpers to manipulate time, just like the Timecop gem:
-
To freeze the current time, use
freeze_time
(ActiveSupport 5.2+):freeze_time
-
To travel to a specific moment in time, use
travel_to
:travel_to 1.hour.from_now
Important
When freezing time with
#travel_to
, time will be frozen (like withfreeze_time
). This means that your application can't detect passage of time by usingTime.now
. -
To travel a re...
Rails: Using require and permit for attributes
Raising errors for required and permitted attributes makes it easier to find errors in your application during development and in tests. Consider this approach if you want to strengthen the params handling in your application.
Example
# config/application.rb
config.action_controller.action_on_unpermitted_parameters = :raise
def user_params
params.require(:user).permit(:full_name)
end
Effects
- This raises an error `Ac...
Testing ActiveRecord callbacks with RSpec
Our preferred way of testing ActiveRecord is to simply create/update/destroy the record and then check if the expected behavior has happened.
We used to bend over backwards to avoid touching the database for this. For this we used a lot of stubbing and tricks like it_should_run_callbacks
.
Today we would rather make a few database queries than have a fragile test full of stubs.
Example
Let's say your User
model creates a first Project
on cr...
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...
Using Low-Level Prompts for High-Accuracy AI Coding
The key to unlocking the full potential of LLMs in coding lies in crafting precise prompts. The main challenge is learning how to structure prompts effectively to guide the model toward accurate results. Further evidence supporting this is the fact that Aider already writes ~70% of its own code (as of 02/2025). However, when starting out, your results may fall short of efficiently generating large portions of your code with the...
Issue Checklist Template
This is a checklist I use to work on issues. For this purpose I extracted several cards related to the makandra process and ported them into a check list and refined that over time a little bit.
This task list is divided by the Gate keeping process in the following steps:
1. Starting a new feature
2. Working on the issue
3. Finishing a feature
4. After Review
Here are some ti...
How to add esbuild to the rails asset pipeline
This are the steps I needed to do to add esbuild to an application that used the vanilla rails asset pipeline with sprockets before.
Preparations
- update Sprockets to version 4
- add a
.nvmrc
with your preferred node version (and install it) - add gems
jsbundling-rails
andforeman
to yourGemfile
:gem 'jsbundling-rails' group :development, :test do gem 'foreman' # ... end
bundle install
- run
bin/rails javascript:install:esbuild
in a console to prepare esbuild. - run `yarn instal...
Using regular expressions in JavaScript
Regular expressions in Javascript are represented by a RegExp
object. There also is a regex literal as in many other languages: /regex/
. However, they are used slightly differently.
Regex literal
- Usage:
/foo+/
- Shorthand for creating a regular expression object
RegExp() object
- Usage:
RegExp("foo+")
ornew RegExp("foo+")
- No surrounding slashes required (they're the literal markers)
- Since the argument is a string, backslashes need to be escaped as well:
RegExp("\\d+")
Gotchas
- Regex objects [never eq...
Faking and testing the network with WebMock
An alternative to this technique is using VCR. VCR allows you to record and replay real HTTP responses, saving you the effort to stub out request/response cycles in close details. If your tests do require close inspection of requests and responses, Webmock is still the way.
WebMock is an alternative to FakeWeb when testing code that uses the network. You sh...
Cucumber: Skipping steps in a scenario outline, based on the current example
In Cucumber, scenario outlines help avoiding tests that are basically the same, except for a few variables (such as different inputs). So far, nothing new.
The problem
Now what if your test should (or should not) do something, like filling in a field only for some tests?
Scenario Outline: ...
When I open the form
And I fill in "Name" with "<name>" # <= we want to do this only occasionally
Then everybody should be happy
Examples:
| name |
| Alice |
| Bob |
You could o...
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...
Jasmine: Use `throwUnless` for testing-library's `waitFor`
testing-library are widely used testing utilities libraries for javascript dependent frontend testing. The main utilities provided are query methods, user interactions, dom expectations and interacting with components of several frontend frameworks, which allows us to worry less about the details happening in the browser and focus more on user centric tests instead!
Some of the time you will find a necessity to use methods like [waitFor
](https://testing-library.com/docs/dom-testing-library/api-async/...
Order in which RSpec processes .rb files
Because your examples should not change global state, you should not need to care about the order in which RSpec processes your .rb
files. However, in some cases you might want to know.
RSpec 3
- Runs
.rb
files in alphabetical order of their file paths by default (or when you specify--order defined
). - You run t...
Rails: Flagging all cookies as secure-only to pass a security audit
Why secure-only cookies used to be necessary
Cookies have an optional secure
flag. It tells the browser to not send the cookie for a non-https request.
It used to be important to activate the secure
flag even on sites that automatically redirect users from http://
to https://
. The reason was that most users will only enter a scheme-less domain like makandra.de
into their location bar, which will default to `http://m...
RSpec: Inferring spec type from file location
RSpec Rails can automatically mix in different behaviors to your tests based on their type
tag, for example enabling you to call get
and
post
in specs with the tag type: :request
.
Alternatively you can skip these tags by setting the config config.infer_spec_type_from_file_location!
in the spec_helper.rb
. This will automatically choose the right type context based on the file location of the test.
For instan...
Threads and processes in a Capybara/Selenium session
TLDR: This card explains which threads and processes interact with each other when you run a Selenium test with Capybara. This will help you understand "impossible" behavior of your tests.
When you run a Rack::Test (non-Javascript) test with Capybara, there is a single process in play. It runs both your test script and the server responding to the user interactions scripted by your test.
A Selenium (Javascript) test has a lot more moving parts:
- One process runs your test script. This is the process you...
Maintaining custom application tasks in Rails
Here are some hints on best practices to maintain your tasks in larger projects.
Rake Tasks vs. Scripts
- The Rails default is using rake tasks for your application tasks. These live in
lib/tasks/*
. - In case you want to avoid rake for your tasks and just use plain ruby scripts, consider
lib/scripts/*
as folder.
Keeping tasks slim
For readability and testing it's easier to keep your tasks slim. We suggest to use folders inside the tasks
or scripts
folder.
Example for a task:
The slim task lib/tasks/gitlab.rb
:
VCR fails if the same request is triggered multiple times
Same requests are recorded only once in vcr. Replaying a test fails, if you trigger the same request multiple times. The error message is somehow confusing, as your cassette contains the request:
An HTTP request has been made that VCR does not know how to handle
If you want to allow to match a request multiple times, you need to configure this explicit with allow_playback_repeats: true. Some exa...