Using rspec to test code prone to external infuence - ruby-on-rails

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.

Related

Outputting HTTP request/response data with RSpec

I have a rails project that serves a JSON API with tests written in RSpec. Often when running specs (request specs, specifically), I’m interested in seeing some details about the HTTP request/response...i.e. the request URL, request body, and response body, ideally JSON pretty-formatted for readability. This isn't for the purposes of documentation but rather as part of the development / debugging process.
I have a helper method I wrote which does this...you just drop a method call into your spec and it prints this stuff out.
But, seems like it would be better if there was a switch that’s part of the running specs. RSpec has custom formatters which I thought might be the right direction, but in trying to build one, I can't figure out how to get access to the request/response objects like you can from inside of your spec.
How can I access the request/response objects in my custom RSpec formatter? Or, perhaps another way to approach the problem?
Here's an approach:
Assuming a rails project, in spec_helper.rb, define a global "after" hook like so:
config.after(:each) do #runs after each example
if ENV['PRINTHTTP']
#use request/response objects here, e.g. puts response.status
end
end
Then, you can conditionally enable by adding the environmental variable on the command-line:
$ PRINTHTTP=1 rspec

Stubbing helpers rspec

I have a method in application_helper that is called admin_rights? to check if a user should be able to add content to the site. I haven't implemented a user system so it only returns true at the moment. But I am trying to test it, but I can't seem to find out how to stub it out so it returns false in the test. The spec checks for a link that should only be visible when admin_rights? returns true. When i test it manually by changing admin_rights? to false, it works as intended. So I am apparently not stubbing it out correctly.
The Spec is:
context "no admin rights" do
before do
page.stub(:admin_rights?).and_return(false)
visit fencers_path
end
it "should not have add fencer link" do
expect(page).not_to have_link('+ Fekter', href: new_fencer_path)
end
end
I'm looking for the correct way to stub it out or an alternative way to test it.
The test case you posted is an acceptance test. It boots up a server instance and goes through the full stack. You should really not rely on stubbing and mocking in these kind of tests. They should ensure that the application as a whole works and should treat your application as a black box. To replace tiny bits of code is a recipe for very brittle acceptance tests. Also if you run your tests with a driver that runs Javascript then there is no chance to get the stubbing to work because the server runs in a different process than your tests do.
You should implement the logic for admin_rights? and then tune your acceptance test-setup that the logic actually returns false. For example sign in with a normal user, which does not have admin rights. In the end you want your acceptance tests to match closely to the real world scenario.

Stubbing File.open with Rspec

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.

How should we test an rjs response in Rails Functional tests?

How can I test a .js.rjs response in rails(2.3.8) functional test ?
You can take the simple path and verify the contents being returned seem correct with a functional test.
However, you'll probably get a lot more value from something like Capybara and Celerity that will let you do real integration testing with a live JavaScript engine and verify the RJS causes the page behave you expect.
http://github.com/jnicklas/capybara/blob/master/README.rdoc
There's assert_select_rjs in case you weren't aware of it (like I was). Its something like an assert_tag for RJS output.

Should I write rails tests with the def or test keyword?

This seems like a simple question but I can't find the answer anywhere. I've noticed that in general, tests in a Ruby on Rails app can be written as:
test "the truth" do
assert true
end
or
def the_truth
assert true
end
It seems newer material writes tests the first way, but I can't seem to find a reason for this. Is one favored over the other? Is one more correct? Thanks.
There has been a shift in recent years from short, abbreviated test names to longer, sentence-like test names. This is partly due to the popularity of RSpec and the concept that tests are specs and should be descriptive.
If you prefer descriptive test names, I highly recommend going with the test method. I find it to be more readable.
test "should not be able to login with invalid password" do
#...
end
def_should_not_be_able_to_login_with_invalid_password
#...
end
Also, because the description is a string it can contain any characters. With def you are limited in which characters you can use.
I believe the first method was implemented starting with Rails 2.2.
As far as I am aware, it simply improves readability of your code (as def can be any function while test is used only in test cases).
Good luck!
As Mike Trpcic suggests you should check out RSpec and Cucumber. I'd like to add that you should also take a look at:
Shoulda (http://github.com/thoughtbot/shoulda/tree/master)
Factory Girl (http://github.com/thoughtbot/factory_girl/tree/master)
Shoulda is a macro framework for writing concise unit tests for your models/controllers, while the second is a replacement for fixtures.
I would suggest doing your testing with either RSpec or Cucumber. I use both to test all my applications. RSpec is used to test the models and controllers, and Cucumber tests the Views (via the included Webrat functionality).

Resources