Selenium Testing Rails with a Focus on File Uploads
I’m just posting this because all the instructions I found for doing file upload testing with selenium involved very few instructions that didn’t cover everything you needed, nor gave actual working examples. In the following I will give some examples of selenium testing a Rails application, with specific instructions on doing file upload testing, because getting that to work was the most tedious part.
Selenium Testing Rails
Our method for using selenium to test a Rails application starts with the Selenium gem.
Step 1: Install the selenium gem.
sudo gem install selenium |
Step 2: Hack the jar in the gem.
There are two ways of doing this. Use this modified jar file if you’re using Selenium-1.0.1.
selenium-server.jar.txtCopy the file to $GEM_HOME/Selenium-1.0.1/lib/selenium/openqa/selenium-server.jar.txt
Or
Add this line to the top of the doType function in core/scripts/selenium-api.js in the jar file:
netscape.security.PrivilegeManager.enablePrivilege("UniversalFileRead"); |
And merge the contents of this jar file into the existing .jar.txt file
capsapi_classes.zipWhile you’re at it…
Go ahead and edit core/RemoteRunner.html and add this line with all the other script lines:
<script language="JavaScript"type="text/javascript"src="lib/cssQuery/cssQuery-p.js"></script> |
There’s a bug in this version of selenium which is missing this line which breaks the ability to use css locators in your commands. That sucks, because css locators are a helluva lot easier to use than xpath locators.
Step 3: Install this selenium rake task into your lib/tasks directory in your rails project. Name it whatever you want. I called it selenium_test.rake. Install the selenium_manager.rb file in lib/. These two files are in the attached zip. selenium_rails.zip
Note that I run mongrel in the test environment on port 8080 for selenium, so hopefully you aren’t already using that port.
Step 4: Create a custom profile for firefox.
Create the directory test/selenium/firefox_profile and create a file named user.js with these contents
1 2 3 4 |
user_pref("capability.principal.codebase.p0.granted", "UniversalFileRead"); user_pref("capability.principal.codebase.p0.id", "http://localhost:8080"); user_pref("capability.principal.codebase.p0.subjectName", ""); user_pref("signed.applets.codebase_principal_support", true); |
The lines above will tell firefox to allow file read access for unsigned javascript from “http://localhost:8080”
Step 5: Write your selenium tests.
I’m just going to give one example test file, and the helper code we use for our selenium tests. I’ll leave it to you to edit it down to something usable for your project.
I’ve changed the names of various models to protect the identity of this project.
Mainly you’re just going to want the setup and teardown functions, which I put into the helper because I hate duplicating code in all the tests.
Put your selenium tests into test/selenium. Below is an example test from that directory.
attachments_test.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
require File.dirname(__FILE__) + '/helper' class AttachmentsTest < Test::Unit::TestCase should 'be able to go to attachments list page from zebra view page' do as_zebra_herder do set_speed '500' browse_to_zebra :fluffy click_list_attachments click_add_attachment see_add_attachment_form submit_attachment test_file('small') see_attachment test_file('small') end end end |
helper.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
require File.dirname(__FILE__) + '/../test_helper' require 'test/unit' require 'selenium' class Test::Unit::TestCase load_all_fixtures include SeleniumHelper # make parent happy def default_test assert true end # # BROWSE helpers # def browse_to_home open('/') end def browse_to_zebra(zebra) browse_to_home click_view_zebra zebra end def browse_to_cantaloupe_list browse_to_home clickAndWait('link=List All Cantaloupes') end # # CLICK helpers # def click_cancel click 'css=.cancel_link a' sleep 1 end def click_list_attachments clickAndWait 'link=List Attachments' end def click_view_zebra(zebra) zebra = zebra_by_fixture_or_name(zebra) clickAndWait "css=.zebras .zebra#zebra_#{zebra.id} .zebra_link a" end def click_add_beaver clickAndWait('css=.add_beaver_link a') end def click_add_cantaloupe click 'link=Add a Cantaloupe' sleep 1 end def click_complete_beaver(description) beaver = get_beaver(description) click("beaver_tail_checkbox_#{beaver.id}") sleep 1 end def click_attach_chips_to_beaver(description) beaver = get_beaver(description) click("attach_chips_link_#{beaver.id}") sleep 1 end def click_add_attachment click 'link=Add Attachment' sleep 1 end def click_edit_cantaloupe(name) click("css=#cantaloupe_#{get_cantaloupe(name).id} .edit_cantaloupe_link a") sleep 1 end def click_delete_cantaloupe(name, opts) cantaloupe = get_cantaloupe(name) unless opts[:ok] choose_cancel_on_next_confirmation end click("css=#cantaloupe_#{cantaloupe.id} .delete_cantaloupe_link a") sleep 1 end # # SEE helpers # def see_attachment(file) file_name = File.basename(file) attachment = get_attachment(file_name) assert_equal file_name, get_text("css=.attachments #attachment_#{attachment.id} .attachment_filename"), 'did not see attachment' assert_equal 'Download', get_text("css=.attachments #attachment_#{attachment.id} .attachment_link a"), 'did not see link to view attachment' end def see_delete_confirmation assert_match /Are you sure/i, get_confirmation end def see_cantaloupe_list assert_equal true, is_element_present('css=.cantaloupes') end def see_zebra_beaver(description) beaver = get_beaver(description) assert_equal description, get_text("css=#beaver_#{beaver.id} .beaver_description"), 'did not see beaver description' assert_equal true, is_element_present("css=#beaver_#{beaver.id} input[type=checkbox]"), 'did not see beaver tail' assert_equal beaver.tail, is_element_present("css=#beaver_#{beaver.id} input[type=checkbox][checked=checked]"), 'did not see beaver tail checked value' end def see_add_beaver_form assert_equal true, is_element_present('css=form#add_beaver_form') assert_equal true, is_element_present('css=input#beaver_description[type=text]'), 'Did not see beaver description field' assert_equal true, is_element_present('css=input[type=submit]'), 'Did not see a submit button' end def see_add_attachment_form assert_equal true, is_element_present('css=form#add_attachment_form input#attachment_uploaded_data[type=file]'), 'did not see file input field' assert_equal true, is_element_present('css=form#add_attachment_form input[type=submit]'), 'did not see submit button' end def see_edit_cantaloupe_form(name) cantaloupe = get_cantaloupe(name) assert_equal true, is_element_present('css=form#edit_cantaloupe_form'), 'did not see edit form' assert_equal true, is_element_present("css=input#cantaloupe_name[type=text][value=#{name}]"), 'did not see cantaloupe name field' assert_equal true, is_element_present('css=input[type=submit]'), 'did not see submit button' end def see_cantaloupe(name) cantaloupe = get_cantaloupe(name) assert_equal name, get_text("css=#cantaloupe_#{cantaloupe.id} .cantaloupe_name") end def see_no_cantaloupe(name) assert_nil Cantaloupe.find_by_name(name) end # # ADD helpers # def add_beaver_to_zebra(zebra, beaver) browse_to_zebra zebra click_add_beaver see_add_beaver_form submit_new_beaver beaver see_zebra_beaver beaver end def add_cantaloupe(name) browse_to_cantaloupe_list click_add_cantaloupe submit_new_cantaloupe name see_cantaloupe name end # # SUBMIT helpers # def submit_attachment(file) type 'attachment_uploaded_data', file.to_s sleep 1 click 'commit' sleep 5 Attachment.clear_active_connections! Attachment.clear_reloadable_connections! end def submit_new_beaver(description) type 'beaver_description', description clickAndWait 'commit' Beaver.clear_active_connections! Beaver.clear_reloadable_connections! end def submit_new_cantaloupe(name) type 'cantaloupe_name', name click 'commit' sleep 1 Cantaloupe.clear_active_connections! Cantaloupe.clear_reloadable_connections! end # # AS helpers # def as_zebra_herder raise 'must call with block' if !block_given? yield end # # FILE helpers # def test_file(file) File.expand_path(File.join(File.dirname(__FILE__), '..', 'fixtures', "selenium.#{file}.test_file")) end # # SETUP/TEARDOWN # def setup @selenium = Selenium::SeleniumDriver.new("localhost", 4444,"*firefox", "http://localhost:8080", 15000); @selenium.start end def teardown @selenium.stop end private def get_attachment(file) puts "Attachment file: #{file}" attachment = Attachment.find_by_filename(file) assert_not_nil attachment attachment end def get_beaver(description) beaver = Beaver.find_by_description(description) assert_not_nil beaver beaver end def get_cantaloupe(name) cantaloupe = Cantaloupe.find_by_name(name) assert_not_nil cantaloupe cantaloupe end def clickAndWait(locator) click locator wait_for_page_to_load('5000') end def zebra_by_fixture_or_name(zebra) zebras(zebra) rescue Zebra.find_by_name(zebra) end end |
Leave a Reply