Stub (...) received unexpected message (...) with (no args) - ruby

I try to write a test using RR. What I need is a stub of a model object.
describe ApplicationController do
subject(:application_controller) { ApplicationController.new }
let(:messages) { ['a1', 'a2', 'a3' ] }
let(:model) { Object.new }
it 'should copy errors to flash' do
stub(model).error_messages { messages }
flash[:error] == nil
subject.copy_errors_to_flash(model)
flash[:error].should == messages
end
end
What I get is
ApplicationController should copy errors to flash
Failure/Error: stub(model).error_messages { messages }
Stub #<Object:0x007ffaa803f930> received unexpected message :error_messages with (no args)
# ./spec/controllers/application_controller_spec.rb:10:in `block (2 levels) in <top (required)>'
I have no idea what am I doing wrong. I think I follow the docs...

You're calling the method 'error_messages on the stub of your model on this line:
stub(model).error_messages { messages }
I presume that you actually want to do something else here, most likely:
model.should_receive(:error_messages).and_return(messages)
which creates a stub method for error_messages and will respond with your messages array whenever your spec tests calls model.error_messages

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?

Use file features on Job DSL

I´m using Job DSL and I would like to download a file, read it, and set some env variables.
def static setSecrets(Job delegate, Map overrides = [:]) {
def liveUsername
def livePassword
def file
new URL('https://path/file').withInputStream { i ->
file.withOutputStream {
it << i
}
}
file.withReader { liveUsername = it.readLines().get(0) }
file.withReader { livePassword = it.readLines().get(1) }
def options = [
IDENTITY_USER: liveUsername,
IDENTITY_PASSWORD: livePassword]
setEnv(delegate, options, overrides)
}
This is the exception that I´m receiving
java.lang.NullPointerException: Cannot invoke method withOutputStream() on null object
Seems like the features of file cannot being used. But being in groovy file I was expecting can use the Job DSL templates and also, all the groovy features.
File is null so is throwing NPE when you call a method on it
def file
...
file.withOutputStream { // BANG!

why each method is invoked?

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.

Resources