Testing a call to an external library in MiniTest - ruby-on-rails

I am working on an application for a blog using Ruby on Rails. I have a model called Essay with a Draper Decorator. I am also using MiniTest::Spec for testing this application. Each Essay has a body which will be stored as Markdown. In the EssayDecorator, I have a method called body which renders the Markdown to html using RedCarpet.
In order to test this method, I wrote the following code:
describe '#body' do
it 'returns html from the markdown' do
essay = FactoryGirl.create(:essay)
#decorated_essay = essay.decorate
markdown = Minitest::Mock.new
#decorated_essay.stub :markdown, markdown do
markdown.expect :render, "<p>Test</p>", [essay.body]
#decorated_essay.send(:body)
markdown.verify
end
end
end
And inside the decorator I have two methods:
def body
markdown.render(model.body).html_safe
end
def markdown
Redcarpet::Markdown.new(Redcarpet::Render::HTML, :autolink => true, :space_after_headers => true)
end
This test passes, but seems weird to me. I don't want to test that RedCarpet is doing its job, I just want to test that I call the render method.
Is there a best practice for mocking out this kind of thing in MiniTest? I am pretty new to using Mocks and very new to using MiniTest.
Thanks, in advance.

IMO this code seems weird because it is not testing the behavior of your code, but it is testing the implementation. If the implementation changed (you stored the HTML in a cache instead of running it through Redcarpet) then this test would fail. This looks over-mocked to me.
I don't want to test that RedCarpet is doing its job, I just want to test that I call the render method.
This is implementation. Why are you running the body through markdown? Because you want hyperlinks to be created from URLs? I'd create a test for that. You want to ensure links have the no-follow attribute? I'd create a test for that. Create tests for why the code is doing something, not how it is doing it.
It is also entirely possible you are missing an abstraction in your application. Something that is responsible for formatting the plain text into HTML. Something that uses Redcarpet or RDiscount or any other library deemed important. The EssayDecorator probably shouldn't be responsible for ensuring that the text was formatted properly, but it may be responsible for telling the right object to apply the formatting.

Related

Build up help for TDD/BDD with RSpec

For about 2 weeks ago i've started learning Ruby, i've made an WebParser / Page WordCounter using 'open-uri' and 'nokogiri'. So i just run up the terminal with 'ruby counter.rb http://test.com word' and i and up with the number of matches of that word, case insensitive so i can grab everything.
SO here I am learning about RSpec, TDD, BDD and all this stuff, and i would like to know how my code could be constructed using RSpec examples and expectations. Ive already read all the documentation, i'm building simples examples to test, etc.
I would like to know if there is anyone that could build my code into RSpec examples and expectations, so I can study what you've done and how you've done.
Here is my code:
require 'open-uri'
require 'nokogiri'
class Counter
def initialize(url)
#url = url
end
def count(word, url)
doc = Nokogiri::HTML(open(url))
doc.css('head').remove
doc.text.scan(/#{word}/i).size
end
end
url, word = ARGV
puts "Found: #{Counter.new(url).count(word, url)} matches."
Hope someone could help me, i'm really into ruby and found this RSpec amazing,
Thanks guys, i'll be studying and waiting!
there's an rspec --init command that will create your boilerplate.
Once you've done this, open spec_helper.rb and require your code file.
By the way, it's a little odd that your initialize accepts a url and assigns it to an instance variable, but the count method takes a url as an argument.
So assuming it's refactored to this:
attr_reader :url
def initialize(url)
#url = url
end
def count(word)
doc = Nokogiri::HTML(open(url)) # this uses the attr_reader
doc.css('head').remove
doc.text.scan(/#{word}/i).size
end
Then you can write a test case like this (this is not a comprehensive coverage, just an example):
describe "Counter" do
let(:url) { "http://some_url" }
let(:counter) { Counter.new url }
it "counts words" do
expect(counter.count("foo")).to(
eq("<whatever the expected result is>")
)
end
end
Using let to set variables is optional. You could also set the variables inside the it ... do block, but you'd have to repeat it for each case.
In addition to .to you have .not_to, and there are many other helpful methods than eq. I recommend reading the RSpec matcher docs to familiarize yourself with these.
It's also worth mentioning that this test case will make an HTTP call which is sometimes desired, and sometimes not. For example if you have many cases and want to run them quickly, then removing HTTP calls will be beneficial. But doing this means that you are no longer actually testing the state of the url. What if the markup changes? Unless your test case actually makes the HTTP call, you won't know for sure.
Still, it's good to know how to remove the HTTP call since the underlying concept ("mocking" or "stubbing") has a lot of uses. Something like this:
it "counts words" do
mock_html = <<-HTML
<!doctype html>
<html lang='en'>
<head></head>
<body>foo</body>
</html>
HTML
expect(Object).to(
receive(:open).with(any_args).at_least(1).times.and_return(mock_html)
)
expect(counter.count("foo")).to eq(1)
expect(counter.count("bar")).to eq(0)
end
any_args is a special term you can use when stubbing methods. You could also use url here since you know what the passed argument is going to be.
For more info, again I'll refer you to RSpec's docs, this time the ones about mocking / stubbing.
Generally you want to focus mostly on input/output of functions. Sometimes you will want to check that another method is called (you'd use mock/stub for this) but it's probably not expected of you to test every single line of code.

Ruby MiniTest test design advice

I am testing http/https content for images that are on a web application running Rails 2.3. The layout is basic. I have my controllers with accompanying test directories including functional and integration folders with tests.
I'm still relatively new to Rails but I was wondering if there was a way to create a file that can test attachments across different controllers. For example, I need to test if whether the images on this web app are either http or https on the About/Projects/People/Accounts/ and Explore controllers. Instead of opening each of the about_controller_test, project_controller_test, etc. files writing the lines of code, I wanted to know if there was a way that I can make a master file that includes all of the controllers.
What I would like to do is to create some sort of module that I can include/extend the controllers in. This makes sense in my head but I run into the problem with naming conventions. If I wanted to make an attachments_test.rb, I wouldn't be able to do this because I don't have an attachments_controller.rb file that maps to it. How can I work around this?
All I would like is to make a file named along the lines of testing_attachment_protocols_test.rb which doesn't map to any controller but where I can put my tests in. I want to have one file to write my tests for different controllers instead of writing 1 test in several files. Would this file be included into test_helper.rb? Any help would be appreciated. Thanks.
---------EDIT-----------------------
I figured out the structure that I basically would like to implement for the test. Below is the general structure of the test that I would like to do.
require File.dirname(__FILE__) + '/../test_helper'
require 'open-uri'
def controller
......
end
class controller; def rescue_action(e) raise e end: end
class controller < Test::Unit::TestCase
def setup
#controller = controller
#request = ActionController::TestRequest.new
#response = ActionController::TestResponse.new
end
test "attachments url pattern for home page should include http when not logged in" do
setup
get :home
assert not_logged_in
#puts #response.body
assert_select("a img", :url =~ /sw.amazonaws/) do |anchor|
assert_equal 'http', anchor.protocol
end
end
end
Now the thing that I have trouble now is what to put in the method call for controller. My goal is to try to be as dynamic as possible. My goal is pretty much to create an attribute accessor method for different Controllers. Any thoughts?

How to mock the redirect to an external URL for a integration/acceptance test?

In my Rails 3 application I have a controller with the following actions (code simplified):
def payment
redirect_to some_url_of_an_external_website
end
# the external website redirects the browser to this action when the payment is done
def payment_callback
#subscription = Subscription.new(:subscription_id => params[:subscription_id])
...
end
In my acceptance test (using steak and RSpec 2), I want to avoid the redirection to this external URL when capybara follows the link pointing to the payment action. Basically I want to mock the route helper payment_path so that it directly points to the payment_call_path with the appropriate subscription_id parameter.
Is this the correct way to do it? If so, how can I mock the payment_path (could not find how to do it)?
Whilst I usually try to avoid mocking in integration tests, here you can do something like this:
MyController.stub!(:payment).and_return('payment received').
Using class_eval as mentioned above will lead to that method being permanently stubbed out across your entire run (if you want this, I'd suggest stubbing it in spec_helper [that's assuming you use a spec_helper]). I find using rspec's mocking/stubbing stuff preferable anyway.
I'm not sure if this is the 'correct' way of doing this, but you can stub any of your application's code to return what you need for your test. So somewhere in your RSpec test you can do something like
MyController.class_eval do
def payment
'payment received'
end
end
Here is an example (see section 'Session Helper Methods') where the #admin? method in ApplicationController is stubbed when a custom RSpec helper module is included into the example group.

How can I test common Rails controller behavior while staying DRY?

I've been writing RSpec tests for some Rails controllers and I've discovered a strong impulse to ensure that the Authlogic authentication is working properly. I also feel like I should be verifying that each action uses the same application-wide layout. However, writing tests for this behavior in every single action seems silly.
What I'd like to see are one-line matchers for filters and layouts, similar to Shoulda's matchers for associations and verifications. Unfortunately, no such matchers seem to be available (except for some Test::Unit macros for filters in this blog post). I'm tempted to just write them myself, but not being able to find anyone who's already done it makes me question whether or not a need for such matchers actually exists.
So my question is, how do you test your controllers' common behavior (if you test it at all), and would one-liner matchers testing filters and layouts be useful? Myself, I'm trying to decide between one-liners in the controller specs combined with speccing the filter explicitly, or just speccing the filter and ignoring the filters and layouts in the controllers (since they're only one line of code anyway).
I don't like the idea of writing specs for filters -- that seems too close to the implementation. If you had used TDD/BDD methods to build your controller from scratch, presumably you'd have written the action first, added some logic (e.g. to handle authentication) and then realized it should go into a filter instead. If your spec is along the lines of "Reject an index request if the current user is not the account user", your spec ought to be able to do something like (aircode):
current_user = Factory.create(:unauthorized)
controller.should_not receive(:index)
get :index
request.should redirect_to(some_safe_path)
And it doesn't matter whether the action is using a filter or not.
You can DRY up controller specs with Rspec macros. So (more hand-waving):
describe MyController do
should_reject_anonymous(self)
...
end
module ControllerMacros
def should_reject_anonymous(test_controller)
describe test_controller, "Authentication" do
it "rejects index" do
test_controller.should_not_receive(:index)
get :index
response.should redirect_to(some_safe_path)
end
end
end
end

XML => HTML with Hpricot and Rails

I have never worked with web services and rails, and obviously this is something I need to learn.
I have chosen to use hpricot because it looks great.
Anyway, _why's been nice enough to provide the following example on the hpricot website:
#!ruby
require 'hpricot'
require 'open-uri'
# load the RedHanded home page
doc = Hpricot(open("http://redhanded.hobix.com/index.html"))
# change the CSS class on links
(doc/"span.entryPermalink").set("class", "newLinks")
# remove the sidebar
(doc/"#sidebar").remove
# print the altered HTML
puts doc
Which looks simple, elegant, and easy peasey.
Works great in Ruby, but my question is: How do I break this up in rails?
I experimented with adding this all to a single controller, but couldn't think of the best way to call it in a view.
So if you were parsing an XML file from a web API and printing it in nice clean HTML with Hpricot, how would you break up the activity over the models, views, and controllers, and what would you put where?
Model, model, model, model, model. Skinny controllers, simple views.
The RedHandedHomePage model does the parsing on initialization, then call 'def render' in the controller, set output to an instance variable, and print that in a view.
I'd probably go for a REST approach and have resources that represent the different entities within the XML file being consumed. Do you have a specific example of the XML that you can give?

Resources