Showing posts with label ruby. Show all posts
Showing posts with label ruby. Show all posts

Tuesday, July 22, 2008

scaffolding tricks

I've been working with a couple of models that have essentially the same column names, but contain data of different types.
Item
Food
PersonalHygiene
I took advantage of the rspec generator to create the scaffolding, but still only use one table for both Food and PersonalHygiene.

script/generate rspec_scaffold item type:string name:string unit:string quantity:string
script/generate rspec_scaffold food name:string unit:string quantity:string
script/generate rspec_scaffold personal_hygiene name:string unit:string quantity:string


Once I did this, I went back and removed the migrations for Food and PersonalHygiene and changed those models to inherit from Item.

rake db:migrate
rake spec

Friday, July 18, 2008

RSpec MVC boundaries: why I was getting back template locations instead of html

I finally found the rock in my shoe. The reason why response.body wasn't returning html is due to a mechanism in

the RSpec library (gem install) that mocks the interaction with the Views by default, specifically the file controller_example_group.rb. As I surmised before, there are some matching methods that don't make much sense in certain contexts; in fact, as stated in the documentation in the code itself, 'we encourage you to explore using the isolation mode and revel in its benefits'. The ideal RSpec testing truly isolates MVC components from each other.

To override that behavior and have a more 'integrated' test experience, simply declare integrate_views in the spec controller context. I have actually read this in another blog somewhere where the term 'integration mode' was used casually (and I didn't know what it meant at the time).
# == Integration mode
#
# To run in this mode, include the +integrate_views+ declaration
# in your controller context:
#
# describe ThingController do
# integrate_views
# ...
#
# In this mode, controller specs are run in the same way that
# rails functional tests run - one set of tests for both the
# controllers and the views. The benefit of this approach is that
# you get wider coverage from each spec. Experienced rails
# developers may find this an easier approach to begin with, however
# we encourage you to explore using the isolation mode and revel
# in its benefits.
Just to reiterate, according to my understanding of the authors' views, to integrate the View elements would be almost defeating the purpose of RSpec's approach to testing. Model, Controller, and View elements should all be tested within each others' spheres.

That being said, here is the code responsible for overriding the 'preferred' behavior:
# Use this to instruct RSpec to render views in your controller examples (Integration Mode).
#
# describe ThingController do
# integrate_views
# ...
#
# See Spec::Rails::Example::ControllerExampleGroup for more information about
# Integration and Isolation modes.
def integrate_views
@integrate_views = true
end
def integrate_views? # :nodoc:
@integrate_views
end
Below, you see where the check is made to see if the View elements are to be included (i.e., unless integrate_views?). You can see in the block that follows that check the code that gets executed if the declaration isn't there.
# === render(options = nil, deprecated_status = nil, &block)
#
# This gets added to the controller's singleton meta class,
# allowing Controller Examples to run in two modes, freely switching
# from context to context.
def render(options=nil, deprecated_status=nil, &block)
unless block_given?
unless integrate_views?
@template.metaclass.class_eval do
define_method :file_exists? do
true
end
define_method :render_file do |*args|
@first_render ||= args[0]
end
end
end
end

if matching_message_expectation_exists(options)
expect_render_mock_proxy.render(options, &block)
@performed_render = true
else
unless matching_stub_exists(options)
super(options, deprecated_status, &block)
end
end
end
Bottom line, the boundaries between the Model, View and Controller elements should really get you to think about what needs to be tested in the spec'ed element... but it's nice to know there are options.

RSpec; what methods and when to use them

In the true nature of a blog, my daily hikes will be somewhat random, driven by much of what I do at work and what tickles my interest at the time. At some point, I will consider consolidation in some organized fashion, but hey... I'm getting ahead of myself.

My current topic deals with RSpec. Suffice to say, I am novice at Ruby/Rails; I love it. You don't need yet another testimonial here, so I won't spend the keystrokes. I really like what I see of RSpec. I am a strong advocate for unit tests. I've spent the last 3 years doing JUnit tests and I will always write unit tests. When I was introduced to RSpec at work, it took me a little time to get my brain around how they worked. It's been somewhat of a challenge to find a comprehensive tutorial on how RSpec works, although there is something out there. A few that I've found useful include:

general RSpec documentation
- http://rspec.info/
- BDD definition and old install instructions for RSpec

tutorials for using RSpec
- an-introduction-to-rspec-part-i
- rspec-tutorial
- developing-a-rails-model-using-bdd-and-rspec-part-1
- rspec-plain-text-story-runner-on-a-fresh-rails-app

reasons for using BDD
- in particular, RSpec
- rspec-and-unit-tests-localizing.html

As I understand it, RSpec is good at
  1. concisely and quickly defining high-level requirements
  2. being easy to read/understand
  3. separating testing dependence from other MVC elements
    1. controller tests never have to touch models or views; you can start testing controller functionality and mock and stub out only the necessary elements of models and views that the controller would interact with in real life
  4. cut down on test repeat (DRY; Don't Repeat Yourself); don't run model tests in the controller when those test are already being run in the model tests

That being said, I think I must be too wet behind the ears still because there are still somethings that I am not fully grasping. There are a whole host of functions that may be used in RSpec test writing. I believe these functions are made available through some Module that is applied by the RSpec gem or plugins.
# example
article = mock_model(Article)
article.should_receive(:author).and_return("Joe")
article.should_receive(:text).and_return("this is the text of the article")
assigns[:article] = article
assigns[:articles] = [article]

One thing that has snagged me in the past is not clearly understanding which of these helper RSpec methods is to be used in a given context. There is a set of helpers that are appropriate to use with Controller tests, another set for View tests, etc. Some might work across all contexts, but I'm finding that not all do (and perhaps don't even make sense).

For example, yesterday I spent a lot of time trying to write RSpec tests for a Controller. I never could figure out why the test didn't work. I was trying to use the 'have_tag' helper.
response.should have_tag('div', /regexp matching expected content/)
I debugged the test and tried to analyze the response object. This object, I believe, is provided by Rails and has nothing to do with RSpec. The response object has a body attribute which I was expecting to contain the generated html that is returned by the application. What I found was the path to the resource instead.
describe "#index" do
it "should return a list of show times" do
get :index
debugger

response.should have_tag("li")
end
end
You will probably need to install support for the ruby debugger if you haven't already in order for the sample above to work. As you would expect, when the test is executed, execution will break when it reaches the line with the string debugger. And all this to explain how I discovered what the value of response.body was when running the Controller test, which was:
"show_times/index"
Why wasn't there html in there? I'm not sure, actually. One theory is that Rails hasn't actually got to the point where it has retrieved the html yet, it's only set where it will pull the content from. What ever the reason is, this whole situation eventually got me thinking, "Do I really want to have that test in the controller anyway?" Isn't that a test of the view element anyway? And we want to be DRY, don't we? Perhaps it is a good thing this test has never worked because it might just be teaching me to pay more attention to what tests I am using and where I am putting those tests.

In fact, when I ran the debugger in the View context, I was getting back html content in the response.body attribute. This would suggest that one should only use have_tag in a View RSpec test.

A good TRail guide to consult would probably be Introduction to Writing Examples using Spec::Rails. After all, when it doubt, read the directions, right?

Getting ready for the hike

I have been dabbling in Ruby/Rails for the last year or so at home. It has been an absolute blast, so much fun, in fact, that I decided to jump from the comfortable waters of Java from my last job to another job steeped in Ruby/Rails. I realize now that I really know very little and have quite a bit to learn regarding the technology.

It's always fun to learn something new... that means I'm having a load of fun right now! At the risk of sounding childish and whiny, the Ruby/Rails technology still shows signs of explosive growth... so much so that it's difficult to find a consolidated set of reference and tutorial documents that reflect how to use the latest gem and plugin options that are available for it.

I hope to provide some good trail logs on my journey that can help other pseudo-novices on the Ruby/Rails trail. It's amazing what a well-placed marker can do and I myself can definitely use as much advice from more veteran hikers on the TRail. Hope to see you at the top.