why each method is invoked? - ruby

I found this example code excerpt from Sinatra README webpage
class Stream
def each
100.times { |i| yield "#{i}\n" }
end
end
get('/') { Stream.new }
when I get to the '/' path, it seems invoke the each method on Stream.new object, why is it?

each is called on a stream object, not on the Stream class. To get a stream object, you need to do Stream.new.

Related

RSpec Mock - class does not implement the instance method: jql. Perhaps you meant to use `class_double` instead?

I want to mock method which use Jira-Ruby gem with jql lib
def call
client = JIRA::Client.new(options)
client.Issue.jql(
"project = #{project_key} AND
status != Done AND
status != Closed AND
status != Cancelled AND
status != Followup",
query_options
)
end
my mock:
let(:jql_options) do
[
"project = TSW-123 AND
status != Done AND
status != Closed AND
status != Cancelled AND
status != Followup",
query_options
]
end
let(:query_options) do
{
start_at: 0,
max_results: 1000
}
end
let(:jira_client) { instance_double(JIRA::Client) }
let(:issue) { instance_double(JIRA::Resource::Issue) }
let(:issue_factory) { instance_double(JIRA::Resource::Issue) }
before do
allow(JIRA::Client).to receive(:new).with(options).and_return(jira_client)
allow(jira_client).to receive(:Issue).and_return(issue_factory)
allow(issue_factory).to receive(:jql).with(*jql_options).and_return(issue)
end
it 'connect to the project' do
expect(subject.call).to eq(project)
end
I'm getting an error:
JIRA::Resource::Issue class does not implement the instance method: jql. Perhaps you meant to use class_double instead?
A few things:
You're using mocking syntax for static values, not objects. My answer to your other question here should help: https://stackoverflow.com/a/60404227/4024628
IMO, the use of instance_double here is fine because you are mocking instances, though if you are going to call the mocked object's methods you often need to define their responses in the instance_double declaration (as mentioned in my linked comment).
Be careful with testing mocked behavior; you should only need to test that the client object is created with certain options, and that jql is called with specific args (meaning, make sure you're validating your code's behavior against breaking changes -- not the gem's behavior).
Your spec is testing that call returns project, but you haven't defined or mocked that anywhere?
You are also defining issue_factory and issue as instance_doubles of the same class, which is inconsistent with the documentation for the classes you're using (typo?).
Something like this might be sufficient:
# You should always name your subject if you are calling methods on it
subject(:instance) { described_class.new }
let(:client) { instance_double(JIRA::Client)
let(:issue_factory) { instance_double(JIRA::Resource::IssueFactory) }
# This likely needs to be an OpenStruct instead since docu says it returns an obj
let(:project) { instance_double(JIRA::Resource::Project) }
before do
allow(client).to receive(:new).with(options) { client }
allow(client).to receive(:Issue) { issue_factory }
allow(issue_factory).to receive(:jql).with(*jql_options) { project }
end
it 'creates client with correct options' do
expect(client).to receive(:new).with(options)
instance.call
end
it 'calls #jql with correct options' do
expect(issue_factory).to receive(:jql).with(*jql_options)
instance.call
end
it { expect(instance.call).to eq(project) }

RSpec check if message has been sent on Slack

I've got a class which is responsible for send a direct message on Slack to every reporter who has not updated his Jira ticket in 2 days. To send message, described class used send_message method (which underneath is HTTParty.post). I'mc using VCR gem but I don't know how to test such behaviour if at the end I'm not getting 2xx or 3xx code.
reporter_reminder_messenger
class ReporterReminderMessenger
def call
fetch_pending.each do |issue|
send_message(issue)
end
end
private
def fetch_pending
#fetch_pending ||= Jira::FetchPendingStatus.new.call
end
def send_message(issue)
MessageSender.new(
user_id: get_user_id(reporter_email(issue)),
message: create_text_message(issue)
).call
end
I was trying to check changes in MessageSender class (fetch_pending.count == 4 from ReporterReminderMessenger.call)
specs
RSpec.describe ReporterReminderMessenger do
let(:reporter_reminder) { ReporterReminderMessenger.new.call }
it 'returns only pending issues' do
VCR.use_cassette('reminder_messenger') do
expect { reporter_reminder }.to change { MessageSender }.by(4)
end
end
end
But I'm getting an error:
NoMethodError:
undefined method `-' for Slack::MessageSender:Class
I believe the issue is that you need to check for MessageSender.count. That said, I would probably not bother using VCR for this and just mock out the requests/responses. Something like:
subject(:reminder) { described_class.new }
let(:call) { reminder.call }
let(:jira_status) { instance_double(Jira::FetchPendingStatus, call: jira_call) }
let(:jira_call) { some_response_hash_or_whatever }
before do
# Ensure any new object created will return the mocked jira_status object
allow(Jira::FetchPendingStatus).to receive(:new) { jira_status }
end
# I'm assuming the intention was to count the number of DB records created?
it { expect { call }.to change { MessageSender.count }.by(4) }
it 'fetches pending JIRA status' do
expect(jira_status).to receive(:call)
call
end
Aside, this code reads more or less like business logic and not really objects that need persisting; you may want to check out CollectiveIdea's Interactor Gem

How to test a function called within a thread in ruby using minitest?

The function fetch_details is called within the thread. It has a post request which raises the CustomError in case of failure. I need to test the
CustomError without actually making the actual post request.
Thread.new {
begin
details = fetch_details(a, b)
param1, param2 = parse_details(details, b)
post_result("Success", url)
rescue CustomError => e
post_result(e.message, url)
end
}
How do I stub the post request inside the fetch_details function to raise the Custom Error?

Can I spy a class?

In the effort to make more readable my test suite, I'm introducing spies in my specs but I'm not sure how to deal with class methods. Is it possible to "spy a class"?
Let's say I have the following sample code
def publish(post)
Publisher.call(post)
post.save
end
And the correspondent spec
it 'delegates the publishing to Publisher' do
let(:blog) { ... }
let(:post) { ... }
expect(Publisher).to receive(:call).with(post).and_call_original
blog.publish(post)
end
Is it possible to rewrite the spec using a spy?
Thanks
You can use spies on partial doubles via allow plus expect:
it 'delegates the publishing to Publisher' do
let(:blog) { ... }
let(:post) { ... }
allow(Publisher).to receive(:call)
blog.publish(post)
expect(Publisher).to have_received(:call).with(post)
end

In MATLAB, can a class method act as a uicontrol callback without being public?

In MATLAB 2008a, is there a way to allow a class method to act as a uicontrol callback function without having to make the method public? Conceptually, the method should not be public because it should never be called by a user of the class. It should only be called as a result of a UI event triggering a callback. However, if I set the method's access to private or protected, the callback doesn't work. My class is derived from hgsetget and is defined using the 2008a classdef syntax.
The uicontrol code looks something like:
methods (Access = public)
function this = MyClass(args)
this.someClassProperty = uicontrol(property1, value1, ... , 'Callback', ...
{#(src, event)myCallbackMethod(this, src, event)});
% the rest of the class constructor code
end
end
The callback code looks like:
methods (Access = private) % This doesn't work because it's private
% It works just fine if I make it public instead, but that's wrong conceptually.
function myCallbackMethod(this, src, event)
% do something
end
end
Storing the function handle of the callback as a private property seems to workaround the problem. Try this:
classdef MyClass
properties
handle;
end
properties (Access=private)
callback;
end
methods
function this = MyClass(args)
this.callback = #myCallbackMethod;
this.handle = uicontrol('Callback', ...
{#(src, event)myCallbackMethod(this, src, event)});
end
end
methods (Access = private)
function myCallbackMethod(this, src, event)
disp('Hello world!');
end
end
end

Resources