SHA Web Team Blog http://shawebteam.posterous.com Web Development Jiu-Jitsu at Cornell University posterous.com Mon, 12 Dec 2011 06:34:00 -0800 VIM Tip Of The Day #4 -- ctags http://shawebteam.posterous.com/vim-tip-of-the-day-4-ctags http://shawebteam.posterous.com/vim-tip-of-the-day-4-ctags

Vim's built-in ctags support allows you go to the source definition of the function under the cursor. This is a huge win when trying to track down the source of a bug that's bubbled up to a higher-level function.

sudo aptitude install exuberant-ctags

To index our shared library source with ctags I have the following in my ~/.vimrc:

" location of my ctags file set tags=~/path/to/common/tags " rebuild ctags file nnoremap <Leader>r :!cd ~/path/to/common; ctags -R .; cd -<CR>

Since I have <Leader> mapped to ',' -- ,r will rebuild my ctags file. Then Ctrl-] will take me to the definition of the function under the cursor. Ctrl-t will return me to where I was. :ta /^_get will find all the functions starting with "_get". :ts will list them and allow you to select which one to go to. :tn and :tp will move the next and previous in the list.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Fri, 18 Nov 2011 06:58:00 -0800 VIM Tip Of The Day #3 -- Map Leader-c to :bd http://shawebteam.posterous.com/vim-tip-of-the-day-3-map-to http://shawebteam.posterous.com/vim-tip-of-the-day-3-map-to

The <Leader> key is a prefix you can use to map custom commands in vim. A number of plugins bind certain functionality to <Leader>+<Some other char>. By default the <Leader> is \. But that is an inconvenient key to type. "," is much more handy so I've configured that as my <Leader> in ~/.vimrc:

" use leader that is easier to type than \ let mapleader=","

To close a buffer you use the :bd (buffer delete) command. I've mapped <Leader>c to :bd to make this more convenient:

" close buffer nnoremap <Leader>c :bd<CR>

Now when I'm in normal mode typing ",c" will close the current buffer.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Thu, 17 Nov 2011 08:59:00 -0800 VIM Tip Of The Day #2 -- Map ; to : http://shawebteam.posterous.com/vim-tip-of-the-day-1-map-to http://shawebteam.posterous.com/vim-tip-of-the-day-1-map-to

Add the following to your ~/.vimrc and restart vim:

" Swap ; and :  Convenient. nnoremap ; : " nnoremap : ; breaks command-t nnoremap \ ;

Vim commands are prefixed with ":" -- but it is annoying to have to press "shift" every time you start a command. It's a lot easier to just press ";".

After moving forward with "f" you can go to the next instance of the char by pressing ";". So I've also remapped \ to ; so that I can do that with \ instead.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Wed, 16 Nov 2011 05:29:00 -0800 VIM Tip Of The Day #1 -- Map jj To Esc http://shawebteam.posterous.com/vim-tip-of-the-day-1-map-jj-to http://shawebteam.posterous.com/vim-tip-of-the-day-1-map-jj-to

For years I tolerated vi as a way to edit files in a terminal. Sure, it was painful, but it filled a need.  Then one day I watched Derek Wyatt's VIM from novice to professional and decided it might be worth actually learning how to use it. After making an effort to learn it it's now the most pleasant and productive editor I've ever used. And I still feel like there's a whole world of stuff I can still learn to make it even more productive. Two other members of our team are using it as well, so I'm going to share some hot tips here that have helped me so far.

Add this to your ~/.vimrc file:

" easy way to get back to normal mode inoremap jj <Esc>

Switching from insert mode back to normal mode is kind of a pain because the Esc key is way off on the edge of the keyboard. This mapping will escape back to normal mode whenever you type 'jj'. J is on the home row of the keyboard, but is an infrequently typed letter. You are pretty much never going to type 'jj' for any other reason, so that makes it an ideal combination for this.

I use a dvorak layout on my keyboard, so I've opted instead for 'tn'. Both those letters are next to each other on the home row.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Mon, 14 Nov 2011 11:22:00 -0800 Scream Driven Development http://shawebteam.posterous.com/scream-driven-development http://shawebteam.posterous.com/scream-driven-development

We have become increasingly aware of the benefits that Test Driven Development(TDD) and Behavior Driven Development(BDD) can bring. The tests define the expected behavior of the system and form a boundary that helps push the development process along in the right direction, while ensuring that it is always in a state that provides some working subset of what the customer wants. With BDD tools like Cucumber you can even go so far as to define the behavior of the system in language that you can review with the customer. But what do you do when the customer can't be relied on to tell you how the system should behave?

It is well known that people can't be relied on to accurately report what they do or what they would do in a given situation. It is best not to listen to users, but to observe them.

We were discussing a system this morning that needs to be ported from a database system that has been discontinued. This is an internal system that sports lots of custom features encoding all kinds of tortured and convoluted business logic. Would it be possible to identify features that the few users of this application no longer use? That would make the job of porting so much easier. But how do we reliably identify these features? Someone suggested that we could just remove them from the system and see if anybody screams.

Brilliant! We can use the screams of the customers to map out a boundary of pain that we can use to specify the form and features of the application. For an internal application with just a few users it seems like a cheap and effective way to go about it. I dubbed it Scream Driven Development, and I think it just might catch on. ;-)

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Mon, 12 Sep 2011 13:42:00 -0700 Making Rails logging easier to read while debugging http://shawebteam.posterous.com/making-rails-logging-easier-to-read-while-deb http://shawebteam.posterous.com/making-rails-logging-easier-to-read-while-deb

Sometimes, when debugging a Rails application’s controller functionality, I’ll tail -f the development log to see what’s going on under the hood. When I need to inspect a variable or test something, I’ll use the logger.info method to send that data to the development log. Unfortunately, Rails is very eager with its reporting, so doing a single page load will often look like this:

me@web:~/wwwroot/rails/myapp$ tail -f log/development.log

messy looking logfile It’s very easy for a single reference to logger.info(“Foo”) to get lost in that fray.
However, with some clever command-line-fu, this problem can be resolved easily. Instead of just writing logger.info(“Foo”), include some unique string prefixing your intended output. Like this:

logger.info "@@@" + my_var.inspect

This can be done multiple times, at various locations in any controllers in use. As long as they log to the development log, it will work.
Back at the command prompt, modify the original command to now be:

me@web:~/wwwroot/rails/myapp$ tail -f log/development.log | grep "@@@"

The output will not live-display only those lines of interest that are prefixed with “@@@”, as they are rendered. This keeps the terminal window much cleaner and saves much time and frustration that would otherwise have been spent searching through a sea of query-dumps.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1457434/230735_10150173297063546_649643545_6809942_5366600_n.jpg http://posterous.com/users/hd2GjyX7YWurU ah496 ah496 ah496
Wed, 03 Aug 2011 05:49:00 -0700 Untitled http://shawebteam.posterous.com/63705786 http://shawebteam.posterous.com/63705786

A RESTful API using JSON, a ruby client gem, and source code and Chef cookbooks on GitHub -- is this really a site by the US Government?

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Thu, 28 Jul 2011 10:53:40 -0700 I'm Still Standing http://shawebteam.posterous.com/im-still-standing http://shawebteam.posterous.com/im-still-standing

Recently I read about some studies showing that sitting for most of the day is harmful to your health. I decided to get a stand-up desk and try standing for most or all of the workday. I was worried that I wouldn't be able to make it through the whole day, so I got a desk that I can easily move up and down, the Steelcase Airtouch. I've found that I do enjoy standing and that it makes me feel more actively engaged in my work. So far I think I'm averaging standing for 2/3 of the workday. Here's a shot of my workstation including the rat's nest of wires that I still haven't cleaned up:

Standup_desk

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Thu, 21 Jul 2011 08:00:00 -0700 Overriding the type of a column in a legacy table in Rails http://shawebteam.posterous.com/overriding-the-type-of-a-column-in-a-legacy-t http://shawebteam.posterous.com/overriding-the-type-of-a-column-in-a-legacy-t

I have a legacy table in Oracle with a number(1,0) column. When I tried to save -1 in this field using ActiveRecord it would always end up with the default value of 0. After lots of debugging effort this blog post helped me discover that Rails was treating the field as :boolean.

puts @MyModel.columns_hash['problem_column'].type
=> :boolean

This post gave me the answer for how to override the column type:

class MyModel < ActiveRecord::Base
  class << columns_hash['problem_column']
    def type
      :integer
    end
  end
end

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Wed, 02 Mar 2011 12:09:16 -0800 Export in SQL Developer 3 is Called Unload http://shawebteam.posterous.com/export-in-sql-developer-3-is-called-unload http://shawebteam.posterous.com/export-in-sql-developer-3-is-called-unload
Unload

Thank you Oracle for wasting more of my time with obscure terminology.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Wed, 02 Mar 2011 06:43:00 -0800 Quote of the Day http://shawebteam.posterous.com/quote-of-the-day http://shawebteam.posterous.com/quote-of-the-day

Simplicity is prerequisite for reliability.

Edsger W.Dijkstra

From How do we tell truths that might hurt?.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Fri, 25 Feb 2011 12:40:00 -0800 Using curl to diagnose HTTP problems http://shawebteam.posterous.com/using-curl-to-diagnose-http-problems http://shawebteam.posterous.com/using-curl-to-diagnose-http-problems

Our broken links report was reporting that cecrtain URLs had been redirected, but we were not redirected when visiting them in the browser. I suspected that it was treating our script differently based on user agent, or something else in the request header. I wanted a tool to work up some ad-hoc HTTP requests. Google and StackOverflow led me to curl. This turned out to work great.

First I tried fiddling with the user agent:

$ curl -v -A 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.16) Gecko/20110107 Iceweasel/3.5.16 (like Firefox/3.5.16)' 'http://www.sce.cornell.edu/sc/programs/index.php?v=126&s=Overview'
* About to connect() to www.sce.cornell.edu port 80 (#0)
*   Trying 132.236.128.13... connected
* Connected to www.sce.cornell.edu (132.236.128.13) port 80 (#0)
> GET /sc/programs/index.php?v=126&s=Overview HTTP/1.1
> User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.16) Gecko/20110107 Iceweasel/3.5.16 (like Firefox/3.5.16)
> Host: www.sce.cornell.edu
> Accept: */*
> 
< HTTP/1.1 301 Moved Permanently
< Date: Fri, 25 Feb 2011 20:33:09 GMT
< Server: Apache/2.2.3 (Red Hat)
< X-Powered-By: PHP/5.1.6
< Set-Cookie: PHPSESSID=jbv7793jpg6jd6ad9ied1lvo25; path=/
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
< Pragma: no-cache
< Location: http://www.sce.cornell.edu/sc/programs/index.php
< Content-Length: 0
< Connection: close
< Content-Type: text/html; charset=utf-8
< 
* Closing connection #0

No dice. Then I tried all the headers I could think of. eg.:

$ curl -v -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' 'http://www.sce.cornell.edu/sc/programs/index.php?v=126&s=Overview'
...
< HTTP/1.1 301 Moved Permanently

Then I tried saving and re-sending cookies:

$ curl -v -c /tmp/coookies.txt 'http://www.sce.cornell.edu/sc/programs/index.php?v=126&s=Overview'

Send the cookies with the request:

$ curl -v -c /tmp/coookies.txt -b /tmp/coookies.txt 'http://www.sce.cornell.edu/sc/programs/index.php?v=126&s=Overview'
...
< HTTP/1.1 301 Moved Permanently

After more fiddling around I realized that it works only after visiting http://www.sce.cornell.edu/sc/programs/index.php first:

$ curl -v -c /tmp/coookies.txt -b /tmp/coookies.txt 'http://www.sce.cornell.edu/sc/programs/index.php'
...
< HTTP/1.1 200 OK

Now the original request succeeds:

$ curl -v -c /tmp/coookies.txt -b /tmp/coookies.txt 'http://www.sce.cornell.edu/sc/programs/index.php?v=126&s=Overview'
* About to connect() to www.sce.cornell.edu port 80 (#0)
*   Trying 132.236.128.13... connected
* Connected to www.sce.cornell.edu (132.236.128.13) port 80 (#0)
> GET /sc/programs/index.php?v=126&s=Overview HTTP/1.1
> User-Agent: curl/7.21.3 (x86_64-pc-linux-gnu) libcurl/7.21.3 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.18 libssh2/1.2.6
> Host: www.sce.cornell.edu
> Accept: */*
> Cookie: PHPSESSID=99e9fktkclj78uqao24cins9s7
> 
< HTTP/1.1 200 OK
< Date: Fri, 25 Feb 2011 20:18:55 GMT
< Server: Apache/2.2.3 (Red Hat)
< X-Powered-By: PHP/5.1.6
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
< Pragma: no-cache
< Connection: close
< Transfer-Encoding: chunked
< Content-Type: text/html; charset=utf-8
...Expected page content follows...

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Mon, 04 Oct 2010 12:37:00 -0700 Changing the DATA_PUMP_DIR http://shawebteam.posterous.com/changing-the-datapumpdir http://shawebteam.posterous.com/changing-the-datapumpdir

I wasted a lot of time assuming the default location for expdp and impdp files was configured in the parameter file. It is apparently configured as a regular Oracle “directory” and can be changed as follows:

SQL> create or replace directory DATA_PUMP_DIR as '/u01/app/oracle/admin/yoursid/dpdump';

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Wed, 25 Aug 2010 06:53:00 -0700 Stubbing the date in Cucumber http://shawebteam.posterous.com/stubbing-the-date-in-cucumber http://shawebteam.posterous.com/stubbing-the-date-in-cucumber

The app I’m working on needs to disallow certain behavior if a deadline has passed. I needed to be able to set the current date to some fixed date so that it would always be the same in relation to other dates specified during testing. I found that this can be done pretty handily by using Mocha to stub Date.today. By combining bits from here and here I came up with:

# features/support/mocha.rb

require 'mocha'
World(Mocha::API)

Before do
  mocha_setup
  ground_hog_day = Date.new(2010, 2, 2)
  Date.stubs(:today).returns(ground_hog_day)
end

After do
  begin
    mocha_verify
  ensure
    mocha_teardown
  end
end

Update

Delorean looks like a nice way to handle this.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Mon, 26 Jul 2010 06:49:00 -0700 Managing views with Rails migrations http://shawebteam.posterous.com/29731038 http://shawebteam.posterous.com/29731038

To manage views with migrations we have hitherto been using execute:

def self.up
  execute ' CREATE OR REPLACE FORCE VIEW BAR AS
               SELECT * FROM foo.bar
               WITH READ ONLY'
end

This worked Ok, but it doesn’t add the view to schema.rb and so the view doesn’t get created in the test environment. I found the rails_sql_views extension which handles this very nicely. I added support for oracle_enhanced. You can install a gem from my code as follows:

$ git clone git://github.com/woodchuck/rails_sql_views.git
$ cd rails_sql_views/
$ rake gem
$ sudo gem install --local pkg/rails_sql_views-0.7.0.gem

Then add the following to your environment.rb:

config.gem "rails_sql_views", :version => ">=0.7.0"

and create your migration:

$ ruby script/generate migration create_bar_view

Now you can use this nice syntax in the migration:

class CreateBarView < ActiveRecord::Migration
  def self.up
    create_view :v_bar, "SELECT * FROM foo.bar WITH READ ONLY"
  end

  def self.down
    drop_view :v_bar
  end
end

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Mon, 15 Mar 2010 06:45:00 -0700 Watchr http://shawebteam.posterous.com/watchr http://shawebteam.posterous.com/watchr

Autotest is a great tool for automatically running your tests when you update a test or one of the files it tests against. Lately I’ve been testing With RSpec and Webrat outside of Rails. Autotest makes a number of assumptions about file naming and location conventions so that it just works in Rails. When not in Rails, however, it isn’t easy to configure it to handle whatever file structure you may have. I just found Watchr, which has the configureability I’m looking for. I created a watchr.rb file like this:

spec_cmd = "spec --color --format nested"
watch('mytests/.*_spec\.rb') { |md| system("#{spec_cmd} #{md[0]}") }
watch('file_to_test.*.html') { |md| system("#{spec_cmd} mytests/mytest_spec.rb") }

Then I start up watchr:

$ watchr mytests/watchr.rb

Now when I save edits to mytests/mytest_spec.rb, file_to_test.html or file_to_test_foo.html watchr will automatically run my RSpec examples.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Fri, 12 Mar 2010 22:00:00 -0800 Integration Testing With RSpec and Webrat http://shawebteam.posterous.com/integration-testing-with-rspec-and-webrat http://shawebteam.posterous.com/integration-testing-with-rspec-and-webrat

Integration testing with Selenium works great, but it is slow and Selenium is hard to set up. It also causes browser windows to open all over my screen unless I do additional work and set it up with Xvfb. So I wanted to look into whether Webrat would speed up my development. Again, Webrat is meant to be used within Rails, so it took some work to figure out how to make it work outside of that environment.

require 'rubygems'
require 'webrat'
require 'spec/test/unit'

include Webrat::Methods
include Webrat::Matchers
Webrat.configure do |config|
  config.mode = :mechanize
end

describe "My Form" do
  it "should echo entered data" do
    visit "http://test.sha.cornell.edu/page/to/test.html"
    fill_in "Name", :with => "Steve Halasz"
    fill_in "Address 1", :with => "11 East Ave."
    fill_in "City", :with => "Ithaca"
    fill_in "Email", :with => "myemail@sha.cornell.edu"
    click_button "Continue"
    ["Steve Halasz", "11 East Ave.", "Ithaca", "myemail@sha.cornell.edu"].each do |text|
      assert_contain text
    end
  end
end

Ooh, that’s pretty. One big advantage over selenium-client is that we can specify where to type and click using user-visible labels rather than css or xpath selectors. This makes the tests much more robust and easy to write and read. It is very clever in its matching. fill_in “Email” works even though the label on the form is actually “* Email address (for confirmation)”.

The other advantage is that this runs in about 11 seconds, while the selenium-client version takes about 22 seconds.

A disadvantage is that Webrat can’t execute JavaScript. Although Selenium doesn’t do this perfectly either.

One problem I ran into is that webrat/mechanize won’t follow meta-refresh redirects. I tried to get around this by monkey patching WWW::Mechanize:

require 'rubygems'
require 'mechanize'

class WWW::Mechanize
  alias original_initialize initialize

  def initialize
    original_initialize
    @follow_meta_refresh = true
  end
end

require 'webrat'
require 'spec/test/unit'

That didn’t solve the problem though, so I ended up following the redirect manually:

def get_meta_refresh_url(session)
  result = session.dom.xpath("//head/meta[@http-equiv='refresh']")
  raise "Unable to find meta-refresh where expected" if result.empty?
  result[0]["content"].gsub /^\d+;url=/, ""
end

describe "My Form" do
  it "should echo entered data" do
    ...
    visit get_meta_refresh_url(webrat_session)
  end
end

If you want to use assert_have_tag you have to include an additional module:

include Webrat::HaveTagMatcher
...
assert_have_tag "title", :content => "My Swell Page"

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Fri, 12 Mar 2010 21:00:00 -0800 Integration Testing With RSpec and Selenium RC http://shawebteam.posterous.com/integration-testing-with-rspec-and-selenium-r http://shawebteam.posterous.com/integration-testing-with-rspec-and-selenium-r

Testing with PHPUnit and Selenium is pretty great because we can verify a complex interaction involving forms on multiple pages. However we’ve started doing most new development in Ruby on Rails. I wanted to see if we could use Ruby’s testing tools to do the same thing. The main challenge is using them outside of the Ruby on Rails environment.

require 'rubygems'
require "selenium/client"
require "selenium/rspec/spec_helper"

describe "My Form" do
  attr_reader :selenium_driver
  alias :page :selenium_driver

  before(:all) do
  @selenium_driver = Selenium::Client::Driver.new \
    :host => "localhost",
    :port => 4444,
    :browser => "*firefox /usr/lib/iceweasel/firefox-bin",
    :url => "http://test.sha.cornell.edu/",
    :timeout_in_seconds => 60
  end

  before(:each) do
    selenium_driver.start_new_browser_session :captureNetworkTraffic => true
  end

  # The system capture needs to happen BEFORE closing the Selenium session
  append_after(:each) do
  @selenium_driver.close_current_browser_session
  end

  it "should echo entered data" do
    page.open "/page/to/test.html"
    page.type "shipname", "Steve Halasz"
    page.type "address1", "11 East Ave."
    page.type "city", "Ithaca"
    page.type "email", "myemail@sha.cornell.edu"
    page.click "submit", :wait_for => :page

    ["Steve Halasz", "11 East Ave.", "Ithaca", "myemail@sha.cornell.edu"].each do |t|
      page.text?(t).should be_true
    end
  end
end

This is an RSpec example which we can run with:

$ spec spec/myform_spec.rb
My Form
- should echo entered data

Finished in 22.862956 seconds

1 example, 0 failures

There is a good bit of setup required here, but I think the test itself reads much more cleanly than the PHPUnit version.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Fri, 12 Mar 2010 20:10:00 -0800 Integration testing with PHPUnit and Selenium RC http://shawebteam.posterous.com/integration-testing-with-phpunit-and-selenium http://shawebteam.posterous.com/integration-testing-with-phpunit-and-selenium

This will be the first of a few posts about integration testing. Our first approach was to use PHPUnit to drive Selenium RC. Selenium RC remote controls an actual web browser. Here’s a simple example which doesn’t even work due to a limitation of Selenium. :–)

require_once 'PHPUnit/Extensions/SeleniumTestCase.php';

class EnkiTest extends PHPUnit_Extensions_SeleniumTestCase {
  protected function setUp() {
    setBrowser('*chrome /usr/lib/firefox-3.0.18/firefox-bin');
    $this->setBrowserUrl('http://www.hotelschool.cornell.edu/');
  }

  public function testImpersonateReferrer() {
    // There is no assertResponse() :( http://jira.seleniumhq.org/browse/SRC-563
    $this->markTestSkipped("This test will fail until assertResponse() is implemented");
    $this->open('http://www.hotelschool.cornell.edu/app/attach/get.html?target=hsnews');
    $this->assertTitleEquals('404 Page Not Found');
    $this->clickAndWait("xpath=//a[@href='/app/site/feedback/']");
    $this->assertTitleEquals('Site Feedback');
  }
}

PHPUnit’s integration with Selenium is described here.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz
Tue, 02 Mar 2010 15:56:00 -0800 Continuous Testing? You’re going to love autotest ZenTest gem! http://shawebteam.posterous.com/blog/2010/03/02/continuous-testing-you%e2%80%99re-going-to-love-autotest-zentest-gem http://shawebteam.posterous.com/blog/2010/03/02/continuous-testing-you%e2%80%99re-going-to-love-autotest-zentest-gem

From all the gems I've ever tried ZenTest is certainly my favorite. I used to run all my tests manually, every time I added a new assertion, a new test case, or altered model/controller/view. With ZenTest's autotest feature that's no longer the case. Indeed, autotest will make your TDD session more productive than ever by automatically rerunning a subset of your test suite each time you touch a file. No kidding! I said you're going to love this gem, didn't I? Autotest is very clever, as soon as you change and save a file (either: model, controller, view, unit test, functional test, ...) autotest will rerun the corresponding dependent tests on its own - yay life is good! Think of it as Continuous Testing. Here is a quick howto install autotest manually:

sudo gem install ZenTest

if you do RoR development you will also need to install:

sudo gem install autotest-rails

or as Rails gem dependencies in “config/environments/test.rb”:

config.gem 'autotest-rails'config.gem 'ZenTest'

Run autotest:

$ cd $ autotestloading autotest/rails/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -I.:lib:test -rubygems -e "%w[test/unit test/unit/address_test.rb test/functional/addresses_controller_test.rb test/unit/user_test.rb test/unit/helpers/addresses_helper_test.rb test/unit/helpers/users_helper_test.rb test/functional/users_controller_test.rb].each { |f| require f }" | unit_diff -uLoaded suite -eStarted................Finished in 0.208859 seconds.16 tests, 22 assertions, 0 failures, 0 errors

Now, go ahead and modify some code in your project so that a test fails. Save the modified file to your workspace and watch how autotest will automatically rerun a subset of the test suite:

/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -I.:lib:test -rubygems -e "%w[test/unit test/functional/addresses_controller_test.rb].each { |f| require f }" | unit_diff -uLoaded suite -eStarted.F.....Finished in 0.739703 seconds.1) Failure:test_should_destroy_address(AddressesControllerTest)[(eval):3:in `each_with_index'/test/functional/addresses_controller_test.rb:40:in `test_should_destroy_address']:"Address.count" didn't change by -2.--- /var/folders/uZ/uZR5uRSiF2SRPhOmq5ScyU+++TI/-Tmp-/expect.752.0        2010-03-01 20:34:02.000000000 -0500+++ /var/folders/uZ/uZR5uRSiF2SRPhOmq5ScyU+++TI/-Tmp-/butwas.752.0        2010-03-01 20:34:02.000000000 -0500@@ -1 +1 @@-+7 tests, 9 assertions, 1 failures, 0 errors

The testing happens on its own, how awesome is that? Side note: when I tried to install ZenTest on a work machine I encountered an exception while running the autotest, something like this:

/.gem/ruby/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:105:in `const_missing': uninitialized constantTest::Unit::TestResult::TestResultFailureSupport (NameError)        from /Library/Ruby/Gems/1.8/gems/test-unit-2.0.6/lib/test/unit/testresult.rb:28

It's due to incompatibility of ZenTest 4.2.1 with the test-unit => 2.0 gem that was already specified as a dependency in our test environment. There are a few things you can do to get around it, either uninstall test-unit entirely or downgrade it to an older version 1.2.3. And yes I hear you "How about my colored test output that comes with the newest test-unit? I'm not going to give it up!" no worries there is another great rubygem that can color tests output called redgreen, phew!

sudo install gem redgreen

or as Rails gem dependency in “config/environments/test.rb”:

config.gem 'redgreen'

I just thought I should toss that in here to save you some troubleshooting, in case you run into a similar issue.

Originally posted by Viola.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/781615/2164857960_2c03241d49_o_edited.jpg http://posterous.com/users/4St0xCPiu0GB Steve Halasz Steve Steve Halasz