Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Tweaking README.

  • Loading branch information...
commit 11e398f050097a0c5c1822bda8903700e01fb049 1 parent ee879fc
@lailsonbm authored
Showing with 13 additions and 12 deletions.
  1. +13 −12 README.markdown
View
25 README.markdown
@@ -1,8 +1,9 @@
-Steak + Javascript pages + Factories
-====================================
+Steak + Javascript + Factories
+==============================
Some days ago, I had a hard time trying to make [Steak](http://github.com/cavalle/steak) play nicely with javascript pages and content on database. This project is a demo app and a tutorial on how achieve this efficiently. Through this page I explain how to configure Steak to use a javascript environment only when needed and to choose a faster way when it doesn't, step by step. If you just need the code, jump straight to end of the page.
+
Steak? What's that?
-------------------
@@ -37,7 +38,7 @@ Now to the action. The easiest way to start off is generating a scaffold:
$ rails generate scaffold person first_name:string last_name:string phone_number:string
$ rake db:migrate
-Nice. Suddenly we have a fully functional contact book, but, as we know we have many contacts, we need some way to search them. It would be great if this search was also fast, so we're going to use javascript to filter the table as we type the contact name (I bet you cannot be faster than that, hum). It may sound complicated, but in fact it's simple: just add a input to enter the contact name and a piece of javascript code (sorry for the bloated Prototype code; I'm a jQuery guy).
+Nice. Suddenly we have a fully functional contact book, but, as we know we have many contacts, we need some way to search them. It would be great if this search was also fast, so we're going to use javascript to filter the listing as we type the contact name (I bet you cannot be faster than that, hum). It may sound complicated, but in fact it's simple: just add a input to enter the contact name and a piece of javascript code (sorry for the bloated Prototype code; I'm a jQuery guy).
# Add this between the <h1> and <table> on app/views/people/index.html
<fieldset>
@@ -126,9 +127,9 @@ Now, let's test the cool search we've built.
end
end
-Again, we use Steak to generate the boilerplate code. In this test, we first add some people to the contact book (this is equivalent to use a factory, but this example is very simple and doesn't need it), then we make sure they all are showed on the initial listing and finally checks that only Johnny remains visible when his name is typed on the search box. We run `rake spec:acceptance` again, but this time the test fails: John, Sarah and Jessica remain on the page even when we are looking only for Johnny. How come? We know the script is working!
+Again, we use Steak to generate the boilerplate code. In this test, we add some people to the contact book (this is equivalent to use a factory, but this example is very simple and doesn't need it), then we make sure they all are showed on the initial listing and finally checks that only Johnny remains visible when his name is typed on the search box. We run `rake spec:acceptance` again, but this time the test fails: John, Sarah and Jessica remain on the page even when we are looking only for Johnny. How come? We know the script is working!
-The problem is with [rack-test](http://github.com/brynary/rack-test). Capybara has many testing drivers and by default it uses rack-test, a fast and reliable API to test rack applications. The problem is rack-test just looks at the HTML code and doesn't run any javascript. So, we need to choose another driver; one that can run javascript. There are some options (look at Capybara's page), but here we'll use the most popular one: Selenium/Webdriver. It runs the page within an actual browser (Firefox by default) and therefore can do everything an user can do. To set it up, add it to the `Gemfile` and a drop line on the acceptance helper.
+The problem is with [rack-test](http://github.com/brynary/rack-test). Capybara has many testing drivers and by default it uses rack-test, a fast and reliable API to test rack applications. The problem is rack-test just looks at the HTML code and doesn't run any javascript. So, we need to choose another driver; one that can run javascript. There are some options (look at [Capybara page](http://github.com/jnicklas/capybara)), but here we'll use the most popular one: [Selenium/Webdriver](http://github.com/rainux/selenium-webdriver). It runs the page within an actual browser (Firefox by default) and therefore can do (almost) everything an user can do. To set it up, add it to the `Gemfile` and change the driver on the acceptance helper.
# Gemfile
group :development, :test do
@@ -141,26 +142,26 @@ The problem is with [rack-test](http://github.com/brynary/rack-test). Capybara h
#...
Capybara.default_driver = :selenium
-Run the test again and you'll see the Rails server running and a Firefox window opening. I really recommend you to use the Firefox 4 (even the beta version), because it starts much faster. But wait, the test still fails! It seems Capybara cannot find Johnny on the page. Let's investigate it by seeing how the page looks to the browser (that's why Lauchy is there).
+Run the test again and you'll see the Rails server running and a Firefox window opening. I really recommend you to use the Firefox 4 (even the beta version), because it starts much faster. But wait, the test still fails! It seems Capybara cannot find Johnny on the page. Let's investigate it by seeing how the page looks on the browser (that's why Lauchy is there).
# spec/acceptance/person_search_spec.rb
# Add this line below visit '/people'
save_and_open_page
-The page is showed we see why Johnny cannot be found: the listing contains just John. But that's not possible! We added four people right before visiting the page. Well, this time the problem is with transactional fixtures. To speed up the tests, Rails wraps each test on a database transaction by default. When the test finishes, the transaction is cancelled, so the fixtures are not messed up and the database is instantly ready for the next test. The problem is that two processes cannot share a transaction, so the server cannot see that the contact book has four people (they will be there only when the transaction was committed, what never happens). To make the test work, we'll have to disable transactional fixtures (and yes, this will slow down tests with fixtures, but currently there is no another way to make this work).
+The page is showed and we see why Johnny cannot be found: the listing contains just John. But that's not possible! We added four people right before visiting the page! Well, this time the problem is with transactional fixtures. To speed up the tests, Rails wraps each test on a database transaction by default. When the test finishes, the transaction is cancelled, so the fixtures are not messed up and the database is instantly ready for the next test. The problem is that two processes cannot share a transaction, so the server cannot see that the contact book has four people (they will be there only when the transaction is committed, what never happens). To make the test work, we'll have to disable transactional fixtures (and yes, this will slow down tests, but currently there's no another way to make this work).
# spec/spec_helper.rb
config.use_transactional_fixtures = false
Phew. Finally we can see the test passing.
-Note: Some people will argue that, since this is an acceptance test, people must be created by visiting the pages and filling up the form, one by one. I agree this looks more like BDD, but there is a pragmatic part of me that prefers to set it up straight on the database because (1) it is much faster and (2) it does not cause the search to fail when the people creation feature is failing.
+NOTE: Some people will argue that, since this is an acceptance test, people must be created by visiting the pages and filling up the form, one by one. I agree this looks more like BDD, but there is a pragmatic part of me that prefers to set it up straight on the database because (1) it is much faster and (2) it does not cause the search to fail when the creation feature is failing.
Marking javascript tests
------------------------
-Okay, the search test is right, but now the creation test is also running with Selenium, even though it doesn't use javascript. That's not good. Webdriver is nice, but it is much slower than rack-test. It would be nice if we could mark the tests that need javascript and run only those with Webdriver. In fact, that is exactly what [Cucumber users do with tags](http://github.com/jnicklas/capybara/blob/master/lib/capybara/cucumber.rb). We can take advantage of [RSpec metadata](http://gist.github.com/448487) to do the same. So, let's indicate that the search uses javascript:
+Okay, the search test is right, but now the creation test is also running with Selenium, even though it doesn't use javascript. That's not good. Selenium is nice, but it is much slower than rack-test. It would be nice if we could mark the tests that need javascript and run only those with Webdriver. In fact, that is exactly what [Cucumber users do with tags](http://github.com/jnicklas/capybara/blob/master/lib/capybara/cucumber.rb). We can take advantage of [RSpec metadata](http://gist.github.com/448487) to do the same. So, let's indicate that the search uses javascript:
# spec/acceptance/person_create_spec.rb
scenario "The listing has four people and I'm looking for John", :js => true do
@@ -186,7 +187,7 @@ Clearing the database
We're almost done. Before we finish, let's see the page that `save_and_open_page` generated again. There are two Johns on the listing, although we created just one on the search test. Strange. But do you remember we are creating another John on the create test? Wow, since we don't use transactional fixtures anymore, all the things we insert on the database remains there, becoming garbage at the end of each test. That is unacceptable; tests must be isolated.
-On this specific case, there were no problems and if we really wanted to be isolated, we could just call `Person.delete_all` at the beginning of each test. But you can see this is a bad idea on anything bigger than that. More one gem to the rescue: database_cleaner. So, that hooks we did above become a little more complex:
+On this specific case, there were no problems and if we really wanted to be isolated, we could just call `Person.delete_all` at the beginning of each test. But you can see this is a bad idea on anything bigger than that. More one gem to the rescue: [database_cleaner](http://github.com/bmabey/database_cleaner). So, that hooks we did above become a little more complex:
# Gemfile
group :development, :test do
@@ -217,12 +218,12 @@ On this specific case, there were no problems and if we really wanted to be isol
end
end
-We choose the transaction strategy by default and change to truncation and Selenium if the test require javascript: it is the best of both worlds. And, as we have a good piece of code now, it is better to move it to the file `spec/support/javascript.rb`. RSpec requires all the files on the `support` directory automatically.
+We choose the `transaction` strategy by default and change to `truncate` if the test requires javascript: it is the best of both worlds. And, as we have a good piece of code now, it is better to move it to the file `spec/support/javascript.rb`. RSpec requires all the files on the `support` directory automatically.
Demo App and Compatibility
--------------------------
-If you're lazy and doesn't want to follow the tutorial above, just download this app and see the specs running (some view specs are failing, but it's a bug on RSpec: they were generated by it on the scaffold). The code was tested with Rails 3.0.0 on Ruby 1.9.2 and 1.8.7.
+If you're lazy and doesn't want to follow the tutorial, just download this app and see the specs running (some view specs are failing, but it's a bug on RSpec: they were generated on the scaffold). The code was tested with Rails 3.0.0 on Ruby 1.9.2 and 1.8.7.
Summary
Please sign in to comment.
Something went wrong with that request. Please try again.