Ruby class 2011 03 29

From Noisebridge
Revision as of 19:11, 29 March 2011 by Cake (talk | contribs) (Created page with '= Testing = == What is testing? == [http://en.wikipedia.org/wiki/Software_testing Software Testing] [http://guides.rubyonrails.org/testing.html Testing in Rails] In short, te…')
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Testing[edit]

What is testing?[edit]

Software Testing

Testing in Rails

In short, testing is making sure your software does what you intend it to do. For smaller applications, e.g.: Rudo to date, you can do this manually just by poking around your application, but as you add more features it becomes harder and harder to make changes without having unintended side effects.

Enter Test::Unit...[edit]

So we've been running these generators, see, and there have been these files appearing in our test folder. Those are tests. Duh. Let's run them:

rake test

Yay, tests passed! Let's have a look, open test/unit/user_test.rb.

assert true

So those of you who went through Ruby Koans will remember assert. It translates to 'make sure the following thing is true'. So in the case of this test it says 'make sure true is true'. A bit of a low bar for our testing suite, I think. Good for our ego and not much else. So let's delete it and replace it with something a little more meaningful.

test "a new user saves" do
  assert User.new.save
end

And run rake again:

rake test

It fails! The user did not save. Take a look at app/models/user.rb. The user didn't save because of our validations. Which is good. Our test should look something more like this:

test "a new user should not save" do
  assert !User.new.save
end

and run our tests again...pass! Still not very useful to us, but it's a step in the right direction. Now let's add another test to make sure it saves when we have the fields it wants:

test "a user should save with valid attributes" do
  assert User.new(:password => 'bill', :email => 'bill').save
end

Run again and success! Now you know what testing is. Let's take a closer look at the structure of our testing folders. Inside test/ we've got:

fixtures/
functional/
integration/
performance/
unit/
test_helper.rb

We were just working on tests inside the unit/ folder, which, as it turns out, will be where you spend roughly 98% of your time when you're writing tests. Unit tests are the smallest scale of testing, and, in general, they map to your models, hence user_test.rb. The models (and the database behind them) are the foundation of your application. Which makes it far-and-above the most important place in your application to ensure proper functionality.

Functional testing is a little higher level. In Rails functional testing maps to your controllers and ensures that you get the response you expect when you hit a certain action. For example, try adding the following to test/functional/sessions_controller_test.rb:

test "invalid user log in should render log in page" do
  post 'create'
  assert_template 'sessions/new'
end

test "valid user log in should redirect to user's tasks" do
  user = User.new(:password => 'blah', :email => 'blah')
  user.save
  post 'create', { :password => 'blah', :email => 'blah' }
  assert_redirected_to user_tasks_path(user)
end

and run. It all passes.

Integration testing is one level higher than functional tests. It's about making sure all functionality across controllers works properly. If you get to the point of writing integration tests then you might have too much time on your hands.

Unlike the other testing areas, performance testing is not about making sure your application does what you expect, but instead about making sure your application does it in a timely fashion. Again, if you find yourself writing performance tests without good reason, you should probably go back and write a few more unit tests.

Which leaves fixtures. Fixtures are sample data that gets added to the database as soon as the tests start running. Say you get tired of typing in task data every time you want to write a new task test. Take a look at test/fixtures/tasks.yml and change it to look like this:

one:
  title:  Do laundry
  done_date: nil

Now inside test/unit/user_test.rb add the following:

test "task should save with title" do
  task = tasks(:one)
  assert task.save
end

Run our tests, and it passes. Unfortunately with the user model and our hashing process it's a little more complicated.

So that's testing with Test::Unit in a nutshell.

Test-Driven Development[edit]

TDD, simply put, is writing your tests before you write your code. Red-Green-Refactor is the catch-phrase of TDD fans. First you write a failing test, then you write the code to make the test pass, and finally you look over your code to see if it can be rearranged better. Rinse, repeat. Behavior-Driven Development(BDD), is slightly higher level. It's less about the specifics of code and more about what your site should do in general, "when I click x I should see y". It's meant to be a more 'plain English' way of writing tests, so that non-coders can write the tests.

Let's look at some tools:

autotest[edit]

sudo gem install autotest-rails

and in your rails directory run:

autotest

now make some changes to your test and save to see what happens. autotest runs tests for the files you are working on every time you save them. Especially helpful for TDD.

rspec[edit]

rspec is an alternative to Test::Unit. It's one of the more popular testing frameworks for testing, largely for the pretty syntax. Let's roll it into our project and see how it looks. Add this to your gemfile:

gem "rspec-rails", "~> 2.4"

and run:

rails g rspec:install

it creates the spec/ folder structure. Rspec uses folders labeled specifically for what they test, so to test our user model let's make a folder test/models/ and create a file user_spec.rb...

cruise_control.rb[edit]

cruise_control.rb is a rails app that implements what is called continuous integration or CI. As your application gets larger, and your testing suite along with it, it can take quite a while to run all of the tests on your local machine. So you set up a CI tool on a server somewhere to monitor your source code repository (e.g.: your project on github). Every time it detects a change it downloads the updated code and runs the tests. It will then notify you of any failing tests by whatever means you specify, email, text, campfire, etc.

rcov/cover_me/simplecov[edit]

code coverage tools. These analyze your tests and tells you how much of your code base is actually being tested. There are various levels of code coverage analysis, of which these are C0, the simplest.

factory_girl[edit]

another popular tool these days. Instead of using the fixtures that come with rails for testing, many developers will instead use factories to generate model instances for their tests.

shoulda[edit]

a handy plugin you can use with Test::Unit or RSpec for some additional ease when testing. It allows you to say things like:

should validate_presence_of :email

cucumber[edit]

A BDD tool, as mentioned above that parses a more plain English syntax for a higher level testing of your application.

selenium[edit]

A front-end testing tool to make sure your views turn out the way you expect.