Testing a job in Dashing - ruby

I am building an application with Dashing/Smashing right now, and I am using rspec to test my code. However, I cannot figure out how to check that send_event is called. I have tried
expect(Sinatra::Application).to receive(:send_event).twice
and
expect(Dashing).to receive(:send_event).twice,
but neither have worked. I am not sure what object is supposed to receive the call to send_event since it lies inside Dashing in app.rb. There is also this issue, talking about the same thing, unanswered on the Dashing GitHub.
Any advice on how to do this would be much appreciated. Thank you!
Update:
I still have not figured out how to do this, but I have discovered that this works:
let(:dummy_class) { Class.new { include Dashing } }
context 'something' do
it 'does something' do
expect(dummy_class).to receive(:send_event).once
dummy_class.send('send_event', 'test', current: 'test')
end
end
However, if I use the method I want to call that contains send_event as opposed to doing dummy_class.send(...), then it does not recognize that the method was called. It must have to do with my test not using the dummy class. I don't know if there is any way to get around this and make it use the dummy class.

I figured it out!
Do not call send_event directly within the job. Call it within some other class, perhaps called EventSender. Then, to test that send_event is called, treat it as though it is an instance method of that class instead of a method of a module. Your code might look like this, for example:
describe 'something' do
context 'something' do
it 'does something' do
happy_es = EventSender.new(...)
expect(happy_es).to receive(:send_event).with(...)
happy_es.method_that_calls_sendevent
end
end
end
Hope this helps someone who is struggling with the same thing. :)

Related

Rspec Double leaking to another example

I am testing a class that makes use of a client that makes external requests and I would like to mock this client, but verify that it gets called, however I am getting a double error.
My test looks like something like this:
describe '#execute' do
let(:attributes) { {foo: 'bar'} }
let(:client_double) { double('client', create: nil) }
let(:use_case) { described.class.new }
before do
allow(Client::Base).to receive(:new).and_return(client_double)
use_case.execute(attributes)
end
it 'creates something' do
expect(Something.find_by(foo: 'bar')).not_to be_nil
end
it 'calls client' do
expect(client).to have_received(:create).with('bar')
end
end
and the first example passes as expected, however rspec keeps breaking in the second example giving me this error:
#<Double "foo"> was originally created in one example but has leaked into another example and can no longer be used. rspec-mocks' doubles are designed to only last for one example, and you need to create a new one in each example you wish to use it for.
someone knows what I can do to fix it?
Reusing Fixtures with Let Methods
In this case, before is actually before(:each), which is reusing the client_double and attributes you defined with the #let helper method. The let commands make those variables functionally equivalent to instance variables within the scope of the described object, so you aren't really testing freshly-created objects in each example.
Some alternatives include:
Refactor to place all your setup into before(:each) without the let statements.
Make your tests DAMP by doing more setup within each example.
Set up new scope for a new #describe, so your test doubles/values aren't being reused.
Use your :before, :after, or :around blocks to reset state between tests, if needed.
Since you don't show the actual class or real code under test, it's hard to offer specific insights into the right way to test the object you're trying to test. It's not even clear why you feel you need to test the collaborator object within a unit test, so you might want to give some thought to that as well.
It turns out I was using a singleton as a client and haven't realized before, so it was trully class caching it through examples. To fix it all I did was mock the instantiate method instead of the new method and everything worked.
So in the end this worked:
allow(Client::Base).to receive(:instantiate).and_return(client_double)

A better way to call methods on an instance

My question has a couple layers to it so please bear with me? I built a module that adds workflows from the Workflow gem to an instance, when you call a method on that instance. It has to be able to receive the description as a Hash or some basic data structure and then turn that into something that puts the described workflow onto the class, at run-time. So everything has to happen at run-time. It's a bit complex to explain what all the crazy requirements are for but it's still a good question, I hope. Anyways, The best I can do to be brief for a context, here, is this:
Build a class and include this module I built.
Create an instance of Your class.
Call the inject_workflow(some_workflow_description) method on the instance. It all must be dynamic.
The tricky part for me is that when I use public_send() or eval() or exec(), I still have to send some nested method calls and it seems like they use 2 different scopes, the class' and Workflow's (the gem). When someone uses the Workflow gem, they hand write these method calls in their class so it scopes everything correctly. The gem gets to have access to the class it creates methods on. The way I'm trying to do it, the user doesn't hand write the methods on the class, they get added to the class via the method shown here. So I wasn't able to get it to work using blocks because I have to do nested block calls e.g.
workflow() do # first method call
# first nested method call. can't access my scope from here
state(:state_name) do
# second nested method call. can't access my scope
event(:event_name, transitions_to: :transition_to_state)
end
end
One of the things I'm trying to do is call the Workflow#state() method n number of times, while nesting the Workflow#event(with, custom_params) 0..n times. The problem for me seems to be that I can't get the right scope when I nest the methods like that.
It works just like I'd like it to (I think...) but I'm not too sure I hit the best implementation. In fact, I think I'll probably get some strong words for what I've done. I tried using public_send() and every other thing I could find to avoid using class_eval() to no avail.
Whenever I attempted to use one of the "better" methods, I couldn't quite get the scope right and sometimes, I was invoking methods on the wrong object, altogether. So I think this is where I need the help, yeah?
This is what a few of the attempts were going for but this is more pseudo-code because I could never get this version or any like it to fly.
# Call this as soon as you can, after .new()
def inject_workflow(description)
public_send :workflow do
description[:workflow][:states].each do |state|
state.map do |name, event|
public_send name.to_sym do # nested call occurs in Workflow gem
# nested call occurs in Workflow gem
public_send :event, event[:name], transitions_to: event[:transitions_to]
end
end
end
end
end
From what I was trying, all these kinds of attempts ended up in the same result, which was my scope isn't what I need because I'm evaluating code in the Workflow gem, not in the module or user's class.
Anyways, here's my implementation. I would really appreciate it if someone could point me in the right direction!
module WorkflowFactory
# ...
def inject_workflow(description)
# Build up an array of strings that will be used to create exactly what
# you would hand-write in your class, if you wanted to use the gem.
description_string_builder = ['include Workflow', 'workflow do']
description[:workflow][:states].each do |state|
state.map do |name, state_description|
if state_description.nil? # if this is a final state...
description_string_builder << "state :#{name}"
else # because it is not a final state, add event information too.
description_string_builder.concat([
"state :#{name} do",
"event :#{state_description[:event]}, transitions_to: :#{state_description[:transitions_to]}",
"end"
])
end
end
end
description_string_builder << "end\n"
begin
# Use class_eval to run that workflow specification by
# passing it off to the workflow gem, just like you would when you use
# the gem normally. I'm pretty sure this is where everyone's head pops...
self.class.class_eval(description_string_builder.join("\n"))
define_singleton_method(:has_workflow?) { true }
rescue Exception => e
define_singleton_method(:has_workflow?) { !!(puts e.backtrace) }
end
end
end
end
# This is the class in question.
class Job
include WorkflowFactory
# ... some interesting code for your class goes here
def next!
current_state.events.#somehow choose the correct event
end
end
# and in some other place where you want your "job" to be able to use a workflow, you have something like this...
job = Job.new
job.done?
# => false
until job.done? do job.next! end
# progresses through the workflow and manages its own state awareness
I started this question off under 300000 lines of text, I swear. Thanks for hanging in there! Here's even more documentation, if you're not asleep yet.
module in my gem

Testing if dynamic method call was received using RSpec

I am trying to test if a dynamic method call was performed using RSpec. Having a bit of trouble.
I have code that looks like :
def self.parse_file
method_name = "parse_#{get_file_type}"
send method_name
end
def self.parse_gz
....
end
Assuming that get_file_type returns "gz", I want to test that parse_gz is called from the parse_file instance method.
Initially, I was thinking something like below, but I think I'm going about it wrong...
Class.should_receive(:parse_gz).with(Class.parse_file)
...that doesn't work
Any help is greatly appreciated...
Your version does not work, because you never call parse_gzwith an argument.Try this:
Class.should_receive(:parse_file)
or
Class.should_receive(:send).with('parse_gz')
Furthermore I would suggest to use the new allow(thing).to receive(method) syntax instead of the old thing.should_receive(method) syntax.

Trouble accessing method from module?

I'm including someone else's module, which seems to be split across multiple files. It looks something like this:
alfred.rb:
require 'alfred/ui'
module Alfred
<code>
end
alfred/ui.rb:
module Alfred
class Logger
def info(msg)
logger.info msg
end
end
end
I'm trying to access the info method from my code, which has require "alfred" at the top, using this syntax:
Alfred::Logger.info("my message")
which is not working, and results in #<NameError: uninitialized constant Alfred::Logger>.
Secondly, I would love if someone could explain how exactly Ruby "constructs" a module that is split across several files like this. Does the code from alfred/ui.rb get included at the end of alfred.rb at the beginning where the require line is? Does it all get put together in some other fashion? Maybe it doesn't even matter, but it seems it would help me wrap my head around it better.
The full code of the module I'm using is at Github in case anyone is interested.
Can someone help explain why I can't use the method this way, and what might be done to fix it?
#info is a instance method, meaning it only exists on instances of the Logger class. If you want to call it directly from your code, you'd call Alfred::Logger.new.info "some message", but that seems a bit odd. Usually that type of thing is used internally by the library.
If it had been defined like a class method:
module Alfred
class Logger
def self.info(msg)
logger.info msg
end
end
end
then you would be able to call it as you're attempting.

How do I stub out a specific file with rspec?

I've searched far and wide and I hope someone can answer this question. I'm using the following code to stub out the 'exists?' method for FileTest in an rspec spec:
it "returns no error if file does exist" do
#loader = MovieLoader.new
lambda {
FileTest.stub!(:exists?).and_return(true)
#loader.load_file
}.should_not raise_error("File Does Not Exist")
end
What I really want to do is to ensure that the existence of a very specific file is stubbed out. I was hoping something like this would do the job:
it "returns no error if file does exist" do
#loader = MovieLoader.new
lambda {
FileTest.stub!(:exists?).with(MovieLoader.data_file).and_return(true)
#loader.load_file
}.should_not raise_error("File Does Not Exist")
end
However, this doesn't seem to be working. I am having a very difficult time finding documentation on what the 'with' method actually does. Perhaps I'm barking up the wrong tree entirely.
Can someone please provide some guidance?
The RSpec stubbing framework leaves a bit to be desired, and this is one of those things. The stub!(:something).with("a thing") ensures that each time the something method is called that it receives "a thing" as the input. If it receives something other than "a thing", RSpec will stop the test and report an error.
I think you can achieve what you want, you'll just have to approach this a little differently. Instead of stubbing out FileTest, you should be stubbing out a method on your #loader instance, and that method would normally call FileTest.exists?. Hopefully this demonstrates what I'm getting at:
class MovieLoader
def load_file
perform_loading if file_exists?(file_path)
end
def file_exists?(path)
FileTest.exists? path
end
end
Your test would then look like:
it "returns no error if file does exist" do
#loader = MovieLoader.new
lambda {
#loader.stub!(:file_exists?).with(MovieLoader.data_file).and_return(true)
#loader.load_file
}.should_not raise_error("File Does Not Exist")
end
Now you are only stubbing one instance of the loader, so other instances will not inherit the stubbed version of file_exists?. If you need to be more fine-grained than that, you'll probably need to use a different stubbing framework, which RSpec supports (stubba, mocha, etc).

Resources