This repo contains some code snippets that I've found useful related to Ruby and Rails
-
-
Save nycdotnet/72859a799053164b66e92a3f65768d16 to your computer and use it in GitHub Desktop.
Quoting aka sanitization:
table = 'my_table'
column = 'my_column'
value = 'some_value'
query = <<-SQL
select * from #{ActiveRecord::Base.connection.quote_table_name(table)}
where #{ActiveRecord::Base.connection.quote_column_name(column)} = #{ActiveRecord::Base.connection.quote(value)}
SQL
# query equals: select * from "my_table"\n where "my_column" = 'some_value'
Asserting that a constructor was called with particular arguments:
# Adds a spy to the constructor call but does not break object initialization
# Note: Do not add a spy to :initialize - Ruby will raise warnings.
expect(MyClass).to receive(:new).with('first arg', 'second arg').and_call_original
Asserting that a thing should throw an error:
it 'throws' do
expect { perform }.to raise_error(StandardError, /some regex or string that matches the error message/)
end
def perform
# do thing that will cause an error
end
# or
it 'also throws' do
expect { 1 / 0 }.to raise_error(StandardError, /divide by zero probably/)
end
Faking a method call to make it throw an error:
expect(whatever).to receive(:method_name).and_raise(StandardError, 'Some horrible error!')
Total fake possibly calling original:
allow(whatever).to receive(:method_name).and_wrap_original do |m, *args|
# m is the method
# *args are the args passed to it.
# you can do anything in here - return a fake result, raise an error, dump a back
# trace with `Thread.current.backtrace` etc.
# To return what the original would have been (aka `and_call_original`) do this:
m.call(*args)
end
Fake an implementation of a method but allow a block it uses to run unchanged:
allow(whatever).to receive(:method_name).and_wrap_original do |m, *args, &block|
# m is the method
# *args are the args passed to it.
block.call
end
Fake using expression evaluated at time of call:
allow(whatever).to receive(:method_name) { return_this_expression_which_is_evaluated_at_time_of_call }
Fakes without an object reference:
allow_any_instance_of(MyClass).to recieve(:method_name).and_return(whatever)
Asserting that a thing should throw an error with specific properties:
expect { perform }.to raise_error do |error|
expect(error).to be_a(GRPC::BadStatus)
expect(error.code).to eq(GRPC::Core::StatusCodes::NOT_FOUND)
end
Partial assertions on method calls:
# this asserts on the first and second arguments, but ignores the rest.
expect(whatever).to receive(:method_name)
.with('first argument', '2nd argument', any_args)
Partial match of hash set (ignoring other keys):
expect(whatever).to match(hash_including({ some_symbolized_key: 'some value', 'some_string_key' => 'other_value' }))
Assert details on an object in a hash (effectively nesting matchers):
expect(whatever).to match(hash_including({
foo: 'bar',
baz: be_an_instance_of(SomeClass) do |x|
expect(x.some_property).to eq 42
end
})
String RegEx match:
expect(whatever).to match(%r{[a-f0-9]{16}}) # sixteen characters of hexadecimal (a-f or 0-9)
Asserting on logging (with Rails)
# before your test code runs, create spies on the appropriate method of the logger (for example one or more of these)
allow(Rails.logger).to receive(:debug)
allow(Rails.logger).to receive(:info)
allow(Rails.logger).to receive(:warn)
allow(Rails.logger).to receive(:error)
#later in your assertions (this is an example - can use any have_received variant)
expect(Rails.logger).to \
have_received(:info).with(hash_including({
message: 'Did some thing',
entity_id: 1234567
}))
Asserting that a method was called on an object where an argument is an instance of a specific type and has specific nested properties:
# the below applies both `.with` calls to the first argument.
expect(my_object).to have_received(:expected_method_name) \
.with(instance_of(ExpectedArgumentType)) \
.with(having_attributes(
some_property: 'abc123',
other_property: 987654,
deep_property: having_attributes(foo: 'bar')
))
Asserting that contents of an array is equal to contents of another array, but don't care about sorting, use the contain_exactly
matcher.
expect([1, 2, 3, 4]).to contain_exactly([4, 2, 3, 1])
Mocking the environment:
# this sets up ENV to be a spy
allow(ENV).to receive(:[]).and_call_original
# this lets you fake only the specified variable and return the value you want (other variables
# will be the "real" one within the test context.
allow(ENV).to receive(:[]).with('MY_VARIABLE_NAME').and_return('my special value')
Mocking RequestStore:
# this sets up the "read" indexer as a spy (same as ENV above)
allow(RequestStore.store).to receive(:[]).and_call_original
# this sets up the "write" indexer as a spy
allow(RequestStore.store).to receive(:[]=).and_call_original
allow(RequestStore).to receive(:clear!).and_call_original
# later code:
expect(RequestStore.store).to have_received(:[]=).with(:whatever_key_name, whatever_the_expected_value_was)
See if something is there:
.blank?
and .present?
are opposites - but need ActiveSupport for .blank?
. .blank?
is true for nil, empty string, and whitespace only strings.