RSpec's `have_attributes` matcher
I’ve been working on rewriting some legacy feature tests that have been unmaintained for several years as system
tests. One of the most convenient methods I have found while creating these tests is have_attributes
.
The RSpec documentation I linked to above is a great overview of the matcher. I have found that it’s useful to matching the result of a service object, processor or even a controller action when we expect the resulting model, or models to have particular attributes set, but do not need to match on the entire object.
Here’s a simple example of have_attributes
in action:
class WidgetCreator
def create
Widget.create(status: :enqueued)
end
end
RSpec.describe WidgetCreator do
describe ".create" do
subject { WidgetCreator.new.create }
it "creates the widget" do
expect { subject }.to change(Widget, :count).by(1)
end
it "marks the widget as enqueued" do
expect(subject).to have_attributes(status: :enqueued)
end
end
end
have_attributes
accepts a single attribute key-value pair, or multiple. If multiple attributes are
provided, then all keys and values must match. This matcher also seems to function against any other
object which exposes attributes using simple setter and getter methods - like ActiveModel::Model
,
Struct
and OpenStruct
. This is based on the
implementation
using __send__
to read each attribute key from the actual object before comparing the value.