Stubbing File.open with Rspec - ruby-on-rails

I'm attempting to stub File.open in order to test a method I have that reads a CSV file.
Here's the model:
class BatchTask
def import(filename)
CSV.read(filename, :row_sep => "\r", :col_sep => ",")
end
end
Here's the spec code:
let(:data) { "title\tsurname\tfirstname\rtitle2\tsurname2\tfirstname2\r"}
let(:result) {[["title","surname","firstname"],["title2","surname2","firstname2"]] }
it "should parse file contents and return a result" do
File.stub(:open).with("file_name","rb") { StringIO.new(data) }
person.import("file_name").should == result
end
However, when I attempt to do this I get (stacktrace):
Errno::ENOENT in 'BatchTask should parse file contents and return a result'
No such file or directory - file_name
/Users/me/app/models/batch_task.rb:4:in `import'
./spec/models/batch_task_spec.rb:10:
Finished in 0.006032 seconds
I've been banging my head against this one and can't figure out what I'm doing wrong. Any help would be greatly appreciated!

It would be helpful to provide a stacktrace, although I will make a guess why it happens. Also, I believe you're approach in here is not good and I will elaborate on how I believe you should test.
Simply, I think that CSV.read does not use File.open. It can use Kernel#open or various other ways a file can be opened in Ruby. You should not stub File.open in a test as this anyway.
There is a great book called Growing Object-Oriented Software Guided by Tests that has a need rule:
Stub only methods on classes/interfaces you control
There is a very simple reason for that. When you're doing test doubles (stub), the primary reason is interface discovery - you want to figure out how the interface of your class should look like and doubles provide you with neat feedback. There is also a secondary reason - stubbing external libraries tends to be quite tricky in some cases (when the library is not extremely stubbable). Thus, you can take a few different approaches here, that I will enumerate:
You can test in integration. You can either create the file in each test and pass the pathname (which is fine).
You can break down the way you're parsing. Instead of passing a filename to CSV.read, find a way when you pass an open File and then stub that in the test. i.e., have your code open the file instead of CSV. That way you can stub it easily
Stub CSV.read instead. That might be a bit dramatic, but in essence, you're not testing your code, you're testing the CSV library. It should already have its own tests and you don't need to test it anyway. Instead, you can rely on the fact that it works and just stub the call to it.
Out of those, I would probably go with the third one. I don't like testing dependencies in my unit tests. But if you want your test to call that code, I suggest finding a way to do the second option (CSV.new(file) should do the trick, but I don't have time to investigate) and finally fall back to #1 if nothing else works.

Related

Why is RuboCop replacing the word "should" with the word "does"?

I'm not sure what is causing it, but the word "should" is being replaced in my code with the word "does". I'm writing a spec in ruby on rails, and I'm trying to follow a BDD approach to my unit tests. One of their recommendations is writing each unit test as a whole sentence, starting with the word, "should", so I wrote the following test:
it 'should not return if there are no existing activities on the project' do
end
The code is being replaced by the following:
it 'does not return if there are no existing activities on the project' do
end
Notice that the word "should" has been replaced. Is this RuboCop? If so, what is the rule I disable to prevent this from happening?
If it's not RuboCop, what could it be?
Well it is actually something Rubocop does for you through RSpec/ExampleWording. Rubocop has an Rspec Style guide, you can check the part about "Should" in Example Docstrings
From the docs:
Do not write 'should' or 'should not' in the beginning of your example
docstrings. The descriptions represent actual functionality, not what
might be happening. Use the third person in the present tense.
So disabling it is actually a bad practice and you should stick to the docs and write your docstrings accordingly.
You can disable RSpec/ExampleWording

Rails, Is there a way to generate Unit Tests from Existing Controllers and methods defined in them?

I was wondering if there is a script that can take existing codebase and generate unit tests for each method in controllers. By default all would be passing since they would be empty and i can remove tests i for methods i dont feel important.
This would save huge time and increase testing. Since i'd have to define only what each method should output and not boilerplate that needs to be written.
You really shouldn't be doing this. Creating pointless tests is technical debt that you don't want. Take some time, go through each controller and write a test (or preferably a few) for each method. You'll thank yourself in the long run.
You can then also use test coverage tools to see which bits still need testing.
You can use shared tests to avoid repetition. So for example with rspec, you could add the following to your spec_helper/rails_helper
def should_be_ok(action)
it "should respond with ok" do
get action.to_sym
expect(response).to be_success
end
end
Then in your controller_spec
describe UserController do
should_be_ok(:index)
should_be_ok(:new)
end

How do you test your Config/Initializer Scripts with Rspec in Rails?

So I was searching online for a solution, but there seems to be scarce information about testing initializers created in Rails.
At the moment, I've written a pretty large API call-n-store in my config/initializers/retrieve_users.rb file. It makes an API request, parses the JSON, and then stores the data as users. Being pretty substantial, I've yet to figure out the cleanest way to test it. Since I need to retrieve the users before any functions are run, I don't believe I can move this script anywhere else (although other suggestions would be welcomed). I have a couple questions about this:
Do I have to wrap this in a function/class to test my code?
Where would I put my spec file?
How would I format it RSpec-style?
Thanks!
I just gave this a shot and have passing rspec tests, I am going to further refactor my code and in the end it will be shorter, but here is a snapshot of what I did, using this link as a guide:
The gist of it is this: make a class in your initializer file with the functions you want, then write tests on the functions in the class.
config/initializers/stripe_event.rb
StripeEvent.configure do |events|
events.subscribe 'charge.dispute.created' do |event|
StripeEventsResponder.charge_dispute_created(event)
end
end
class StripeEventsResponder
def self.charge_dispute_created(event)
StripeMailer.admin_dispute_created(event.data.object).deliver
end
end
spec/config/initializers/stripe_events_spec.rb
require 'spec_helper'
describe StripeEventsResponder do
before { StripeMock.start }
after { StripeMock.stop }
after { ActionMailer::Base.deliveries.clear }
describe '#charge_dispute_created' do
it "sends one email" do
event = StripeMock.mock_webhook_event('charge.dispute.created')
StripeEventsResponder.charge_dispute_created(event)
expect(ActionMailer::Base.deliveries.count).to eq(1)
end
it "sends the email to the admin" do
event = StripeMock.mock_webhook_event('charge.dispute.created')
StripeEventsResponder.charge_dispute_created(event)
expect(ActionMailer::Base.deliveries.last.to).to eq(["admin#example.com"])
end
end
end
Seems like this would be tricky, since the initializers are invoked when loading the environment, which happens before the examples are run.
I think you've already figured out the most important step: move the code into another unit where it can be tested outside of the Rails initialization process. This could be a class or a module. Mock out the network dependencies using webmock.
Where should this code reside? See this (now quite ancient) answer by no less than Yehuda Katz himself. Put the spec file in the corresponding part of the tree; i.e. if the unit ends up in lib/my_class.rb, the spec goes in spec/lib/my_class_spec.rb. You may need to modify spec/rails_helper to load the spec file.

Test Redcarpet Markdown helper

I have a markdown helper that looks like this in application_helper.rb.
How would you go about writing a test for something like this? Also, is this the best way to generate simple HTML from markdown?
Thanks!
def markdown(text)
Redcarpet::Markdown.new(Redcarpet::Render::HTML, :autolink => true, :space_after_headers => true).render(text).html_safe
end
Two options come to mind:
Stub out Redcarpet::Markdown and create an expectation of what it should receive:
expect(Redcarpet::Markdown).to receive(:new).with(...) # `with` args omitted for brevity
markdown(text)
The problem here is that the coupling between the method and the test is very high. It's very difficult to refactor the method without breaking the test, even though the method actually functions the same.
Assert the correctness of the output text directly:
expect(markdown(text)).to eq 'This is markdownified'
This allows you to refactor better, but it seems more like an integration test, not a unit test, and is almost testing Redcarpet more than your own method. Unit tests should usually assume that the external methods they call work correctly.
This is one of those cases where testing feels redundant to me since the method is just a thin wrapper around another, and I would not be against not unit testing it, and rather ensuring that you have an integration test—if this is by having your spec be an integration test, then that's probably okay.
It's ultimately all up to you and your TDD workflow and overall testing principles though.

Using rspec to test code prone to external infuence

I'm using rspec to test a code that may fail depending on the change of a site structure (the external influence I mentioned). I would like to write an example that involves "should raise an error" but I'm not sure if rspec is the right tool to test code in such situations. Could someone point me in some direction?
Thanks in advance
You could write custom matchers
Something like :
site.should_have_valid_structure
Spec::Matchers.define :have_structure
match do |actual|
actual.structure == Site::VALID_STRUCTURE
end
end
Mock the external influence so you can test it properly (if the external influence is a Web page or other HTTP request, WebMock and VCR are great for this). Your tests should not rely on anything external functioning properly -- or improperly. See http://marnen.github.com/webmock-presentation/webmock.html for an overview I wrote last year.

Resources