I need to test the following helper:
def display_all_courses
#courses = Course.all
output = ""
for course in #courses do
output << content_tag(:li, :id => course.title.gsub(" ", "-").downcase.strip) do
concat content_tag(:h1, course.title)
concat link_to("Edit", edit_course_path(course))
end
end
return output
end
and I'm wondering if there is a way that I can test the output of this. Basically, I just want to test that the helper gets me the correct number of li elements, and maybe the case when there aren't any courses.
My first thought is to do something like this:
describe DashboardHelper do
describe display_all_courses do
it "should return an list of all the courses" do
7.times{Factory(:course)
html = helper.display_all_courses
html.should have_selector(:li)
end
end
end
and this works just fine. However, if I add the :count option to the have_selector call it suddenly fails, can anybody help me figure out why that is?
I believe what you were looking for was have_tag and with_tag RSpec helpers
describe DashboardHelper do
describe display_all_courses do
it "should return an list of all the courses" do
7.times{ Factory(:course) }
helper.display_all_courses.should have_tag('ul') do
with_tag('li', 3)
end
end
end
end
Maybe it could help to treat the html as xml?
In that case this link could help.
It defines a matcher have_xml which could be just what you need.
Although i understand it would be nicer if the have_tag would just work on strings too.
Clearly a template is the best way to do this.
Related
I am trying to write two RSpec tests for two different problems that are much more advanced that what I'm used to writing.
What I'm trying to test within my controller:
def index
#buildings ||= building_class.active.where(place: current_place)
end
My attempt at writing the RSpec test:
describe 'GET :index' do
it "assigns #buildings" do
#buildings ||= building_class.active.where(place: current_place)
get :index
expect(assigns(:buildings)).to eq([building])
end
end
This test failed and wouldn't even run so I know I'm missing something.
My second test is needing to test the returned value of a class method. Here is what I am needing to test within the controller:
def class_name
ABC::Accountant::Business
end
Here is my attempt at testing this method:
describe "class name returns ABC::Accountant::Business" do
subject do
expect(subject.class_name).to eq(ABC::Accountant::Business)
end
end
For the first test I would do something like this:
First, I would move that .active.where(place: current_place) to a scope (I'm guessing building_class returns Building or something like that):
class Building << ApplicationRecord
scope :active_in, -> (place) { active.where(place: place)
Then it's easier to stub for the test
describe 'GET :index' do
it "assigns #buildings" do
scoped_buildings = double(:buildings)
expect(Building).to receive(:active_in).and_return(scoped_buildings)
get :index
expect(assigns(:buildings)).to eq(scoped_buildings)
end
end
Then your controller will do
#buildings ||= building_class.active_in(current_place)
This way you are testing two things: that the controller actually calls the scope and that the controller assigns the returned value on the #buildings variable (you don't really need to test the actual buidlings, you can test the scope on the model spec).
Personally, I feel like it would be better to do something like #buildings = current_place.active_buildings using the same idea of the scope to test that you are getting the active buildings of the current place.
EDIT: if you can't modify your controller, then the stubbing is a little different and it implies some chaining of methods that I don't like to explicitly test.
scoped_buildings = double(:buildings)
controller.stub_chain(:building_class, :active, :where).and_return(scoped_building)
get :index
expect(assings(:buildings)).to eq scoped_buildings
Note that now your test depends on a specific implementation and testing implementation is a bad practice, one should test behaviour and not implementation.
For the second, I guess something like this should work:
describe ".class_name" do
it "returns ABC::Accountant::Business" do
expect(controller.class_name).to eq(ABC::Accountant::Business)
end
end
IMHO, that the method's name if confusing, class_name gives the idea that it returns a string, you are not returnin a name, you are returning a class. Maybe you can change that method to resource_class or something less confusing.
I have a helper page_title_default in ApplicationHelper:
def page_title_default(options = {})
t '.title', options
end
Now I want to test it like this:
describe '#page_title' do
subject { page_title }
it { ... }
end
end
This results in the following error:
Cannot use t(".title") shortcut because path is not available
According to this post it should be possible to stub the #virtual_path variable like this:
helper.instance_variable_set(:#virtual_path, "admin.path.form")
But this doesn't seem to help: While I am able to stub it and then to call something like helper.t '.something' directly in the test, it doesn't work for the translation helper which is used in the page_title_default method (which still has #virtual_path set to nil). So it seems it's not the same instance of translation helper. But how can I find the page_title_default method one's?
How about something like:
RSpec.describe PageHelper, :type => :helper do
describe "#page_title_default" do
before do
allow(helper).to receive(:t).with(".title", {}) { "Hello!" }
end
subject { helper.page_title_default }
it { is_expected.to eq "Hello!" }
end
end
We're stubbing the "translated" string returned here to decouple the spec of helper from "real" translations, which may appear to be fragile for the test of PageHelper itself - the tests would fail every time you change the translations of ".title".
On the other hand - if you change the key used, eg. from ".title" to ".default_title" it should fail, because it is change of behaviour.
I think the proper text displayed should be tested on different level of test (integration tests, to be specific). Please, check the following answer.
Hope that helps!
I'm very rigorous when it comes to my HTML markup and I follow a strict coding convention for forms, lists, etc...
I would like to include reusable test in my RSpec tests that would allow for me call a form test from any other test and target it directly to the page or URL that I'm testing.
Something like this:
# spec/helpers/form_tester.rb
describe FormTester
it "should check to see if all the text fields have an ID prefix of 'input-'" do
... #form should be valid ...
should be true
end
end
# spec/requests/user_form.rb
describe UserForm
it "should validate the form" do
#form = find(:tag,'form')
# call the FormTester method
end
end
Any ideas on how todo this? I'm using Rails 3.1, with RSpec, Capybara and FactoryGirl.
Use shared examples. In you case, something like this may work:
# spec/shared_examples_for_form.rb
shared_examples 'a form' do
describe 'validation' do
it 'should be validated' do
form.should_be valid
end
end
end
# spec/requests/user_form.rb
describe UserForm
it_behaves_like 'a form' do
let(:form) { find(:tag, 'form') }
end
end
It's also possible to pass parameters to shared examples and place the shared examples inside spec/support. Just have a read at the documentation.
Shared examples are great, but you've got at least two serious problems here.
First: why are you giving your form fields IDs at all? You've already got perfectly good selectors: just use input[name='whatever']. And even if you are giving them IDs, don't put a prefix on them: input#whatever or just #whatever is probably a more sensible selector in your CSS than #input-whatever. By being overspecific on your selector names, you're most likely making your CSS and JavaScript harder to write than they have to be.
Second: don't use RSpec to test your views. RSpec is at its best when confined to models. Cucumber is better for anything user-facing.
I have been using webrat with my rails 3.0.7 project and have been trying to write a test that uses regular expressions. It should fail, yet the if statement seems to corrupt the outcome.
it "should have 'microposts' if 0 posted" do
visit root_path
response.should have_selector('span.microposts') do |span|
if span =~ /\d/
span.should contain('hello')
end
end
end
The test is trying to confirm that the test, yet it still succeeds when an 'if' statement is used. It is trying to match the content '0 microposts'.
When I use these lines in replacement of the 'if' statement:
response.should have_selector('span.microposts') do |counter|
counter.should contain(/hello/)
end
I get the test to finally fail like it is supposed to, but then I don't get to verify the number in front of the content inside of the span like I was trying to above.
Does webrat not hand if statements well, or am I doing something wrong?
Thanks in advance!
It seems you are missing an end but I don't see how that wouldn't bomb.
it "should have 'microposts' if 0 posted" do
visit root_path
response.should have_selector('span.microposts') do |span|
if span =~ /\d/
span.should contain('hello')
end
end
end
module ApplicationHelper
def title(page_title, show_title = true)
content_for(:title) do
page_title.to_s
end
#show_title = show_title
end
end
Anyone knows how can I test this helper using test unit?
For any helper testing in rails, you always start in tests/unit/helpers.
Since this is a ApplicationHelper, use the file called application_helper_test.rb
In that file you can have something like
test "displays page title" do
assert_equal "April 2010", title("April 2010", false)
end
You can test whatever is returned in a helper by just calling the method as usual, and asserting something is sent back.
Not knowing what you are doing, personally, there is too much going on in this method, but that could just be me.
I'd break these two out, so that your helper is just returning a page_title and another one is returning a "show_title" whatever that is. or is that like your switch to say " I should show this title on a page"?