RSpec: Run a single spec (Example or ExampleGroup)
RSpec allows you to mark a single Example/ExampleGroup so that only this will be run. This is very useful when using a test runner like guard.
Add the following config to spec/spec_helper.rb
:
RSpec.configure do |config|
# These two settings work together to allow you to limit a spec run
# to individual examples or groups you care about by tagging them with
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
# get run.
config.filter_run_including :focus => true
config.run_all_when_everything_filtere...
Capybara: Accessing the parent of an element
If you already selected an element and want to get its parent, you can call find(:xpath, '..')
on it.
To get the grand-parent element, call find(:xpath, '../..')
.
Example
Find a link which contains a twitter icon and check that it links to the correct page:
<a href="http://twitter.com/">
<i class="icon is-twitter"></i>
</a>
link = page.find("a .icon.is-twitter").find(:xpath, '..')
expect(link[:href]).to eq("http://twitter.com/")
About XPath
There is a good overview on XPath syntax on [w3schools](htt...
RSpec: Running examples by name (or running a single shared example)
When an Rspec example fails, I usually investigate by running that example again using rspec <file:line>
. However, this does not work with shared examples, since Rspec doesn't know in which context the shared example should be run.
But there is a different way: You can run the shared example using the -e
, --example
option. It takes a string value and runs all scenarios containing that substring in their full description.
This allows you to run a single uniquely named example, all examples with
similar names, all the examples in a u...
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...
Fixing flaky E2E tests
An end-to-end test (E2E test) is a script that remote-controls a web browser with tools like Selenium WebDriver. This card shows basic techniques for fixing a flaky E2E test suite that sometimes passes and sometimes fails.
Although many examples in this card use Ruby, Cucumber and Selenium, the techniques are applicable to all languages and testing tools.
Why tests are flaky
Your tests probably look like this:
When I click on A
And I click on B
And I click on C
Then I should see effects of C
A test like this works fine...
Async control flow in JavaScript: Promises, Microtasks, async/await
Slides for Henning's talk on Sep 21st 2017.
Understanding sync vs. async control flow
Talking to synchronous (or "blocking") API
print('script start')
html = get('/foo')
print(html)
print('script end')
Script outputs 'script start'
, (long delay), '<html>...</html>'
, 'script end'
.
Talking to asynchronous (or "evented") API
print('script start')
get('foo', done: function(html) {
print(html)
})
print('script end')
Script outputs 'script start'
, 'script end'
, (long ...
Rails: namespacing models with table_name_prefix instead of table_name
When you want to group rails models of a logical context, namespaces are your friend. However, if you have a lot of classes in the same namespace it might be tedious to specify the table name for each class seperately:
class Accounting::Invoice < ApplicationRecord
self.table_name = 'accounting_invoices'
...
end
class Accounting::Payment < ApplicationRecord
self.table_name = 'accounting_payments'
...
end
A replacement for the self.table_name
-assignment is the table_name_prefix
in the module definition:
modu...
RSpec: How to define classes for specs
RSpec allows defining methods inside describe
/context
blocks which will only exist inside them.
However, classes (or any constants, for that matter) will not be affected by this. If you define them in your specs, they will exist globally. This is because of how RSpec works (short story: instance_eval
).
Negative example:
describe Notifier do
class TestRecord < ApplicationRecord # DO NOT do this!
# ...
end
let(:record) { TestRecord.new }
it { ... }
end
# TestRecord will exist here, outside of the spec!
D...
How to change the class in FactoryBot traits
FactoryBot allows a :class
option to its factory
definitions, to set the class to construct. However, this option is not supported for trait
s.
Most often, you can just define a nested factory instead of a trait, and use the :class
option there.
factory :message do
factory :reply, class: Message::Reply do
# ...
end
end
If you need/want to use traits instead (for example, it might make more sense semantically), you can not use a :class
on a trait
.
In that case, use initialize_with
to define the record's constr...
How to avoid ActiveRecord::EnvironmentMismatchError on "rails db:drop"
After loading a staging dump into development, you might get an ActiveRecord::EnvironmentMismatchError
when trying to replace the database (like rails db:drop
, rails db:schema:load
).
$ rails db:drop
rails aborted!
ActiveRecord::EnvironmentMismatchError: You are attempting to modify a database that was last run in `staging` environment.
You are running in `development` environment. If you are sure you want to continue, first set the environment using:
bin/rails db:environment:set RAILS_ENV=development
Starting with R...
Dealing with I18n::InvalidPluralizationData errors
When localizing model attributes via I18n you may run into errors like this:
I18n::InvalidPluralizationData: translation data { ... } can not be used with :count => 1. key 'one' is missing.
They seem to appear out of the blue and the error message is more confusing than helpful.
TL;DR A model (e.g. Post
) is lacking an attribute (e.g. thread
) translation.
Fix it by adding a translation for that model's attribute (attributes.post.thread
). The error message reveals the (wrongly) located I18n data (from `attributes.thread...
How to pair a Bose Quiet Comfort 35 with your Ubuntu computer
You need to disable "Bluetooth low energy", then follow these core steps:
- Make sure the headphones are in pairing mode.
- Pair with System Settings > Bluetooth. On 16.04 I had to choose "proceed without pairing" and enter the PIN "0000"
- Select & test the headphones in System Settings > Sound. Choose High Fidelity Playback (A2DP Sink).
I also had to install a package with sudo apt-get install pulseaudio-module-bluetooth
and load it with pactl load-module module-bluetooth-discover
. Put the latter command into ~/.bashrc
or you'll...
Ruby: A small summary of what return, break and next means for blocks
Summary
- Use
return
to return from a method.return
accepts a value that will be the return value of the method call. - Use
break
to quit from a block and from the method that yielded to the block.break
accepts a value that supplies the result of the expression it is “breaking” out of. - Use
next
to skip the rest of the current iteration.next
accepts an argument that will be the result of that block iteration.
The following method will serve as an example in the details below:
def example
puts yield
puts ...
Custom Ruby method Enumerable#count_by (use for quick statistics)
I frequently find myself needing a combination of group_by
, count
and sort
for quick statistics. Here's a method on Enumerable
that combines the three:
module Enumerable
def count_by(&block)
group_by(&block)
.transform_values(&:count)
.sort_by(&:last)
.to_h
end
end
Just paste that snippet into a Rails console and use #count_by
now!
Usage examples
- Number of email addresses by domain:
> User.all.count_by { |user| user.email.sub /^.*@/, '' }
=> { "sina.cn"=>2, ..., "hotmail.com"=>128...
Rspec: around(:all) and around(:each) hook execution order
Background
-
before(:all)
runs the block once before all of the examples. -
before(:each)
runs the block once before each of your specs.
Summary
-
around(:suite)
does not exist. -
around(:all)
runs afterbefore(:all)
and beforeafter(:all)
. -
around(:each)
runs beforebefore(:each)
and afterafter(:each)
.
As this is not 100% obvious (and not yet documented) it is written down in this card. In RSpec 3 :each
has the alias :example
and :all
the alias :context
.
Example
RSpec.configure do |config|
...
Vagrant: create entry for box in .ssh/config
If you want to ssh into your vagrant box without switching into the project directory and typing vagrant ssh
, you can also create an entry directly in ~/.ssh/config
. This will allow you to use ssh <my-box>
from anywhere. Simply paste the information provided by vagrant ssh-config
to your ~/.ssh/config
-File: vagrant ssh-config >> ~/.ssh/config
Example:
$ vagrant ssh-config
Host foobar-dev
HostName 127.0.0.1
User vagrant
Port 2200
UserKnownHostsFile /dev/null
StrictHostKeyChecking no
PasswordAuthentication no
Id...
How to mount a legacy database to migrate data
There are many approaches out there how you can import data from a legacy application to a new application. Here is an approach which opens two database connections and uses active record for the legacy system, too:
1. Add you database information to you config/database.yml
.
data_migration:
database: your_application_data_migration
2. Create a separate application record for the data migration, e.g. in app/data_migration/migration_record.rb
. You will need to create an app/data_migration.rb
class first.
class DataMig...
Understanding Scope in Ruby
Scope is all about where something is visible. It’s all about what (variables, constants, methods) is available to you at a given moment. If you understand scope well enough, you should be able to point at any line of your Ruby program and tell which variables are available in that context, and more importantly, which ones are not.
The article gives detailed explanation on the variable scope in ruby with examples that are easy to understand. Every ruby developer should at least know the first part of the article by heart. The second half ...
JavaScript: Polyfill native Promise API with jQuery Deferreds
You should prefer native promises to jQuery's Deferreds. Native promises are much faster than their jQuery equivalent.
Native promises are supported on all browsers except IE <=11, Android <= 4.4 and iOS <= 7.
If you need Promise
support for these old browsers y...
JavaScript: How to query the state of a Promise
Native promises have no methods to inspect their state.
You can use the promiseState
function below to check whether a promise is fulfilled, rejected or still pending:
promiseState(promise, function(state) {
// `state` now either "pending", "fulfilled" or "rejected"
});
Note that the callback passed to promiseState
will be called asynchronously in the next [microtask](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/...
How to use a local gem in your Gemfile
You can use local copies of gems in your Gemfile
like this:
gem 'spreewald', path: '~/gems/spreewald'
As soon as you have bundled your project with the local copy of the gem, all code changes in the copy will be available on your project. So you can for example set a debugger or add console output in the gem and use it from your project.
If you checked out the gem with your versioning tool, you can easily reset your changes afterwards or make a pull request for the gem if you improved it.
Don't commit a Gemfile with local path...
IRB: last return value
In the ruby shell (IRB) and rails console the return value of the previous command is saved in _
(underscore). This might come in handy if you forgot to save the value to a variable and further want to use it.
Example:
irb(main):001:0> 1 + 2
=> 3
irb(main):002:0> _
=> 3
irb(main):003:0> a = _
=> 3
RSpec's hash_including matcher does not support nesting
You can not use the hash_including
argument matcher with a nested hash:
describe 'user' do
let(:user) { {id: 1, name: 'Foo', thread: {id: 1, title: 'Bar'} }
it do
expect(user).to match(
hash_including(
id: 1, thread: {id: 1}
)
)
end
end
The example will fail and returns a not very helpful error message:
expected {:id => 1, :name => "Foo", :thread => {:id => 1, :title => "Bar"}} to...
Selenium cannot obtain stable Firefox connection
When using geordi for integration tests you might get the following error when trying to run geordi cucumber
:
unable to obtain stable firefox connection in 60 seconds (127.0.0.1:7055) (Selenium::WebDriver::Error::WebDriverError)
This means, that the vnc window the tests is talking to has no proper firefox version running. To figure out the issue this might help you:
- Check if the
.firefox-version
(e.g.24.0
) is the same as~/bin/firefoxes/24.0/firefox
says in the browser - Maybe [rest...