Top Banner
Testing/Testing In Rails 1 Alan and Saskia 2/8/2008
36
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Testing/Testing In Rails 1

Alan and Saskia2/8/2008

Page 2: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Outline

• Testing In General• Unit Testing and Test First!• Integration Testing• Functional Testing• Fixture• Mocking with Mocha

Page 3: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Testing In General (1)

• Formal Definition– Testing is the process of finding differences

between the expected behavior specified by system models and the observed behavior of the implemented system.

– The goal is to design tests that exercise defects in the system and to reveal problems

Page 4: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Testing In General (2)

• Alternative Definition– Testing has to demonstrate that faults are not

present at all.• Almost impossible to show• May lead to the selection of test data that have a low

probability of causing the program to fail

• Even more alternative (Wilson Bilkovich)– Writing applications without tests makes you a

bad person, incapable of love.

Page 5: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Types of Testing (1)

Page 6: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Types of Testing (2)

Page 7: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Unit Testing in Ruby

• Unit Testing– Testing small units of codes (normally methods)

• Test::Unit– Ruby’s built-in testing framework– xUnit family (Java’s Junit, .NET’s Nunit)

• Concepts– Assertions: comparison of expected value and result of an

expression– Failure: assertion failure– Error: exception or runtime error– Green vs red bar

Page 8: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Unit Testing in Ruby (2)require 'roman'require 'test/unit'class TestRoman < Test::Unit::TestCase

def setupend

def test_simpleassert_equal("i", Roman.new(1).to_s)assert_equal("ix", Roman.new(9).to_s)

end

def teardownend

end

<<Class inclusion

<<super class

<<naming convention

<<assertions

<<code to be run before all test methods

<<code to be run after all test methods

Page 9: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Assertions (1)

• assert(boolean, [ message ] )– Fails if boolean is false or nil.

• assert_nil(obj, [ message ] )• assert_not_nil(obj, [ message ] )

– Expects obj to be (not) nil.• assert_equal(expected, actual, [ message ] )• assert_not_equal(expected, actual, [ message ] )

– Expects obj to equal/not equal expected, using ==.• assert_in_delta(expected_float, actual_float, delta, [ message

] )– Expects that the actual floating-point value is within delta

of the expected value.

Page 10: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Assertions (2)• assert_raise(Exception, . . . ) { block }• assert_nothing_raised(Exception, . . . ) { block }

– Expects the block to (not) raise one of the listed exceptions.• assert_instance_of(klass, obj, [ message ] )• assert_kind_of(klass, obj, [ message ] )

– Expects obj to be a kind/instance of klass.• assert_respond_to(obj,message, [ message ] )

– Expects obj to respond to message (a symbol).• assert_match(regexp, string, [ message ] )• assert_no_match(regexp, string, [ message ] )

– Expects string to (not) match regexp.• assert_same(expected, actual, [ message ] )• assert_not_same(expected, actual, [ message ] )

– Expects expected.equal?(actual).

Page 11: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Assertions (3)• assert_operator(obj1, operator, obj2, [ message ] )

– Expects the result of sending the message operator to obj1 with parameter obj2 to be true.

• assert_throws(expected_symbol, [ message ] ) { block }– Expects the block to throw the given symbol.

• assert_send(send_array, [ message ] )– Sends the message in send_array[1] to the receiver in send_array[0],

passing the rest of send_array as arguments. Expects the return value to be true.

• flunk(message="Flunked")– Always fail.

Page 12: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Test First!

• What is it about?– Write the test cases first. Then write minimal

codes to pass the tests– Write more tests. Write more minimal codes to

pass the tests– Iterate the process till all functional requirements

are satisfied

Page 13: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Test First Guidelines• The name of the test should describe the requirement of the code• There should be at least one test for each requirement of the code. Each

possible path through of the code is a different requirement• Only write the simplest possible code to get the test to pass, if you know

this code to be incomplete, write another test that demonstrates what else the code needs to do

• A test should be similar to sample code, in that it should be clear to someone unfamiliar with the code as to how the code is intended to be used

• If a test seems too large, see if you can break it down into smaller tests• If you seem to be writing a lot of code for one little test, see if there are

other related tests you could write first, that would not require as much code

• More at http://www.xprogramming.com/xpmag/testFirstGuidelines.htm

Page 14: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Test First Demo

Page 15: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Unit Testing in Rails

• In rails, unit testing is geared towards testing of individual functions created in a model

• For each model generated, a test file is automatically created in tests/unit directory

• script/generate model product name:string description:stringexists app/models/

exists test/unit/exists test/fixtures/create app/models/product.rbcreate test/unit/product_test.rb

….

Page 16: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Outline• Testing In General• Unit Testing and Test First!• Integration Testing• Functional Testing• Fixture• Mocking with Mocha

Testing/Testing In Rails 1 Saskia

Page 17: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Types of Testing (1)

Page 18: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Integration Testing in RoR• Integration Testing

– story-level, tests interactions & interfaces of various actions supported by the application, across all controllers

– Find bugs with session management and routing, triggered by certain cruft accumulating in a user’s session

• In 'test/integration':– Create test file with 'script/generate integration_test stories_test'

– Class IntegrationTest inherits from Test::Unit

Page 19: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

• Story set describes how the application ought to function– Interfaces: exchange of right data – Interaction: employment of right components

• Example: signing up, getting account, multiple users

• Testing time grows with number of integrated units– Bottom-Up: Integrate tested Units to subsystems as new

components

Integration Testing in RoR

Page 20: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Integration Testing in RoR

require "#{File.dirname(__FILE__)}/../test_helper"class StoriesTest < ActionController::IntegrationTest

fixtures :users, :accounts def test_stories get "/signup" assert_response :success assert_template "signup/index" post "/signup", :name => "Bob", :user_name => "bob", :password => "secret" assert_response :redirect follow_redirect! assert_response :success assert_template "account/index" end

<<class inclusion

<<Integration Test Class

<<naming convention

<<assertions

File 'stories_test.rb' in 'test/integration':

Page 21: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Integration Testing in Ruby (2)

Started.F..EFinished in 0.01 seconds.

1) Failure:where/which testwhat went wrong2) Error:where/which testerror_typewhat went wrong

5 tests, 9 assertions, 1 failure, 1 error.

<<Each spot represents a test, sorted alphabetically, 3 values: . means successful test (pass) F means failed test E means an error occurred

Run by: rake test_integration or ruby stories_test.rb

Output:

<<Listing of failures & errors: Details on where and whatMoves on with first wrong assertion.

Page 22: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Types of Testing (1)

Page 23: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Functional Testing in Ruby

• Functional Testing– single controllers and interactions between the

models it employs

• In directory 'test/function':

– 'functional_controller_test.rb' stubs for each 'script/generate controller'

Page 24: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Functional Testing in RoRrequire File.dirname(__FILE__) + '/../test_helper'require 'home_controller'class HomeControllerTest < Test::Unit::TestCase def setup @controller = HomeController.new @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new end def test_index get :index assert_response :success endend

<< grab HomeController for testing

<< test of main index page<< get method simulates request on action called index

.

<< setup of 3 typical funtional Test objects:* controller to be tested

* TestRequest to simulate web request

* TestResponse to provide information about test request

<< assertion assures that request successful

5 request types supported as methods in Rails:get, post, put, head, delete

Page 25: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Fixtures in RoR• Fixtures: automatically created sample data

– To fill testing database with predefined data before tests run– database independent– Require explicit loading with fixtures method within test class

• In directory 'test/fixtures':

– fixture stubs for each 'script/generate model' • Formats

– YAML: good readability, file extension '.yml' – CSV: comma-separated value file format, '.csv', easy reuse of

existing data in spreadsheet/database (save/export as CSV)

Page 26: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

YAML-Fixtures

# This is a YAML comment!david: id: 1 name: David Dwarf birthday: 0010-01-01 profession: slingshoter

goliath: id: 2 name: Goliath Giant birthday: 0000-02-22 profession: terrorizer

<< list of values

<< name1

each fixture: * 'fixture-name'* list of colon-separated key/value pairs

<< name2

File 'persons.yml' of YAML-Fixtures in 'text/fixtures':

<< fixture-records separated by blank line

Page 27: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

CSV-Fixtures

id, username, password, intelligent, comments1, dhow, imstupid, false, I laugh ""Ha! Ho! Hu!"" 2, admin, ihatedhows, true, What a mess!3, nobody, ilovetomock, true, "Nobody mocks you" 4, nulpe,, false,

<< list of value-records,one record per line

CSV fixture names automatically generated: “model-name”-”counter”

File 'users.csv' of CSV-Fixtures in 'text/fixtures':<< header: first line, comma-

separated list of fields

format: * each cell stripped of outward facing spaces * comma as data: cell must be encased in quotes * quote as data: must escape it with 2nd quote * no blank lines * nulls achived by placing comma

users-1users-2...

Page 28: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Fixtures in Action# allow this test to hook into the Rails framework

require File.dirname(__FILE__) + '/../test_helper'

# need to include a User for testing

require 'user'

class UserTest < Test::Unit::TestCase

fixtures :users

def test_count_fixtures

assert_equal 5, User.count

end

end

<< fixture-load method: * destroys any data in users table * loads fixture data into users table * dumps the data into a variable for direct access

automatic load of the fixtures at the start of each test method

Page 29: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Hashes with Fixtures

...

fixtures :users, :person

def test_user

users(:nobody)

users(:nobody).id

end

def test_person

david = users(:david).find

email( david.girlfriend.email, david.illegitimate_children )

end

...

Fixtures are basically Hash objects:- direct access via generated local variable of the test case

<< returns Hash for fixture named david

<< returns id-property of david

Fixtures can get form of the original class:- access to methods only available to that class

<< using find method to grab "real" david as Person

<<now: access to methods only available to a Person class

<< load multiple fixtures separated by commas

Page 30: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Mocking with Mocha• Problem with Fixtures

– Fixtures makes testing slow (engage the actual DB)

– Fixtures allow invalid data– Maintainability Challenges– Fixtures are brittle

• That’s why we need Mocha (http://mocha.rubyforge.org/) – It provides stubs and mocks to simulate data,

especially data in the database

Page 31: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Installing Mocha

• sudo gem install mocha• In rails, include in test/test_helper.rb

require ‘mocha’

Page 32: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Do Not Mock My Stub

• Stubbing (State Verification)– Stubbing a method is all about replacing the method with

code that returns a specified result (or perhaps raises a specified exception).

• Mocking (Behavior Verification)– Mocking a method is all about asserting that a method has

been called (perhaps with particular parameters).• it’s difficult (or impossible?) to do mocking without stubbing -

you need to return from the mocked method, so that the code under test can complete execution

• http://www.martinfowler.com/articles/mocksArentStubs.html

Page 33: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Stubbing example (stub)require 'test/unit'require 'rubygems'require 'mocha'

class TestProduct < Test::Unit::TestCase def test_product product = stub('ipod_product', :manufacturer => 'ipod', :price => 100) assert_equal 'ipod', product.manufacturer assert_equal 100, product.price endend

Page 34: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

More stubbing example (stubs)class View attr :document def initialize(document) @document = document end def print() if document.print puts "Excellent!" true else puts "Bummer." false end endend

require 'test/unit'require 'rubygems'require 'mocha'

class ViewTest < Test::Unit::TestCase

def test_should_return_false_for_failed_print document = stub("my document") document.stubs(:print).returns(true)

ui = View.new(document) assert ui.print end

end

Page 35: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Mocking example (expects)class Enterprise

def initialize(dilithium) @dilithium = dilithium end

def go(warp_factor) warp_factor.times { @dilithium.nuke(:anti_matter) } end

end

require 'test/unit'require 'rubygems'require 'mocha'

class EnterpriseTest < Test::Unit::TestCase

def test_should_boldly_go dilithium = mock() dilithium.expects(:nuke).with(:anti_matter).at_least_once enterprise = Enterprise.new(dilithium) enterprise.go(2) end

end

Page 36: Testing/Testing In Rails 1 Alan and Saskia 2/8/2008.

Expects More Mocking

• Methods in expects:– at_least, at_least_once, at_most, at_most_once,

in_sequence, multiple_yields, never, once, raises, returns, then, times, when, with, yields