Skip to main content

DB2 and Ruby on Rails, Part 3: Testing with DB2 and Ruby on Rails

Ruby on Rails built-in test facilities and common DB2 diagnostic tools for DB2

John Chun, DB2 Advanced Support Specialist, IBM, Software Group
John Chun is a specialist of the DB2 Advanced Support team working in the area of application development and tooling. He has worked in the IBM DBT Toronto lab for 7 years resolving DB2 application issues with various languages including Java, C, C++, Perl, REXX, C# and others. John has worked on a number of projects involving the DB2 CLI and OLEDB driver, as well as the .NET data provider. John is a DB2 Certified Solutions Expert and Certified Websphere Administrator.
Alex Pitigoi, Advisory Software Engineer, IBM
Alex Pitigoi is an advisory software engineer at the IBM Toronto Lab. He has worked on various software development projects in the Information Management since 1998, focusing on Web technologies and database administration. Most recently, he drove the development of the SQLModel project, now incorporated into the Eclipse Data Tools Project, as well as the overall architecture for the database administrative Web Tools across multiple IBM data servers. Alex also worked on the DB2 Satellite Administration Center, the IBM Express Runtime, and lead the development of the first set of Web Tools delivered for DB2. His current focus is IBM's data servers enablement for new open source technologies (Ruby, Python, PHP).
Christine Law, DB2 Advanced Support Specialist, IBM, Software Group
Christine Law is senior DB2 specialist and an IBM Certified Expert at the IBM Toronto Lab, where she is responsible for resolving DB2 applications problems and defects. She has extensive application development experience on Linux, UNIX and Windows platforms with different programming languages and scripting languages, specializing in JDBC, SQLJ, stored procedures and embedded SQL. Her recent interests includes Open Source technologies such as AJAX and Ruby.
Naomi Ngan, Senior Software Engineer, Autonomy
Naomi Ngan completed an Honours degree in Computer Science and Statistics at the University of Toronto in Canada in 2000. Upon graduating, she joined IBM, where she was responsible for resolving IBM DB2 RDBMS product defects and problems in an application development environment. After almost 4 years at IBM, she went on to the Ernest Gallo Clinic and Research Center at UCSF to develop Bioinformatics software. This includes designing and developing database objects, JSPs, Java standalone applications and stored procedures within an XML environment on Linux and Windows platforms. Currently, she is a senior software engineer at Autonomy Corporation developing J2EE enterprise software. She has in-depth knowledge of application development and tooling for DB2 and holds numerous IBM and Sun developer certifications in DB2, XML, WebSphere, and Java/J2EE.

Summary:  A key requirement in agile application development is incremental integration of the code that ensures regression testing. The Ruby on Rails framework is making all this effort easier than ever. Rails not only has support for unit and functional testing, akin to well-known rapid application development environments, but it also silently generates most of the infrastructure, directories, test files and test cases through smart templates. In this third article in the DB2 and Ruby on Rails series, we'll take advantage of the Rails built-in test framework, applied in the context of the Team Room demo sample associated with the second article.

View more content in this series

Date:  21 Jun 2007
Level:  Intermediate
Activity:  1704 views

Introduction

In Part 1 of the DB2 and Ruby on Rails series, we introduced the IBM_DB Ruby driver, Rails migration, and a Team Room application. In Part 2, you learned how to build on the existing Team Room application to take advantage of DB2® pureXML™ support in a Rails application.

Now that the Team Room application has grown and is becoming more complex, it is time to ensure that the application is doing what is expected. Naturally, the next step is to test our Team Room application. This third article shows you how testing works in a DB2 on Rails environment, and how easy it is to write tests within the Ruby on Rails framework.

The Rails framework provides a convenient means of testing web applications thanks to its built-in support for testing. In Rails, you can write tests after you have a core application up and running, or as you write your application, and even before you start writing the application [referred to as Test Driven Development (TDD)].

Testing the Team Room application

When you create a new Rails project, Rails generates the test infrastructure for you. If you go into our Rails Team Room project directory in D:\rails\teamroom\, you will notice the \test subdirectory. Under \test, you will see five existing directories and a helper file:


Listing 1. Test directory contents
/unit
/functional
/fixtures
/integration
/mocks
test_helper.rb

You put all your tests into the /test directory, and place specific tests into each of the five subdirectories according to nature and function of each. Each of the components within the /test directory is explained below:

Unit

In Rails, tests written to test models are called unit tests. Generally, you write one unit test per model. In a unit test, you test anything that could possibly break your model. Basic tests should include the testing of validation code and assertions, and database operations such as Create, Read, Update and Delete (CRUD).

Functional

Tests written to test controllers are called functional tests. They test the application at a higher level than unit tests. Again, generally you write one functional test for each controller you have. Examples of functional tests include testing for a successful Web request, redirection to the correct page, proper authentication, and the correct responses for specific actions.

Fixtures

A fixture specifies the contents within a model. You specify data in a fixture, and tell Rails to load the data when you run the unit tests. This is somewhat similar to the import or load function in DB2.

Mocks

A mock is a "fake" object that allows you to focus on testing important features within the scope of the Rails project, rather than on interactions with external systems that you have no control over. For example, our Team Room application notifies our users by email when new documents are added to specific subjects to which they've subscribed. We do not have an email validation system to verify that the email supplied by a user is in fact a valid email address. Rather, we rely on an error report from the email account provider or the domain server to validate the email addresses provided by our users. In order to test our Team Room notification function without accessing the external network or having an email validation system in place, we can write mock test methods.

Integration

An integration test is a test that spans multiple controllers and actions. As the name suggests, this test ensures that different controllers and actions work together as expected. Integration tests are generally more complete as they cover high-level interactions between various components within the Rails application.

test_helper.rb

The test_helper.rb file establishes a number of default Rails behaviors common to multiple test cases. For example, in test_helper.rb, notice that the RAILS_ENV environmental variable is set to "test" so that Rails uses the test database (configured in the database.yml file) for testing. All unit tests load the test_helper.rb file in Rails. Custom assertions can also be defined in test_helper.rb so that multiple tests within the same Rails application can share and use these custom assertions.

Setting up various database environments

In Part 1 of the DB2 and Ruby on Rails series, we edited the D:\rails\teamroom\config\database.yml file in order to make a connection to the DB2 XMLDB development database. Below the "development" section in the YAML file, there are two additional sections named "test" and "production" that we did not explicitly fill out. As the name suggests, you specify your test database environment in the "test" section, and your production database environment in the "production" section. In real life, your development or test environment may point to the same database, but your production environment would certainly be separated from the development or test database. Now, let's create our TESTDB test database and set up our test environment in the database.yml file.

Create the TESTDB database:

db2 create db testdb using codeset utf-8 territory us

Edit the database.yml to look like the following:


Listing 2. The database.yml file
# IBM DB2 Database configuration file
#
# Install the IBM DB2 driver and get assistance from:
# http://www.alphaworks.ibm.com/tech/db2onrails
development:
  adapter:      ibm_db
  database:     xmldb
  username:     user
  password:     secret
  schema:       teamroom
  application:  TeamRoom
  account:      devuser
  workstation:  devbox
# == remote TCP/IP connection (required when no local database catalog entry available)
# host:         bigserver     // fully qualified hostname or IP address
# port:         50000         // data server TCP/IP port number

test:
  adapter:      ibm_db
  database:     testdb
  username:     testuser
  password:     secret
  schema:       teamroom
  application:  TeamRoom
  account:      testuser
  workstation:  testbox

production:
  adapter:      ibm_db
  database:     proddb
  username:     produser
  password:     secret
  schema:       teamroom
  application:  TeamRoom
  account:      produser
  workstation:  prodbox

For testing purposes, we want to ensure the test database has the same table structures as the development database. Instead of having to manage DDL scripts to load the schema, we can use Rake commands to help with creating the test environment. Running rake --tasks command shows a few of the relevant commands for building or emptying out the test database:


Listing 3. Rake --tasks output
rake db:test:clone             # Recreate the test database from the current 
                               # environment's  database schema
rake db:test:clone_structure   # Recreate the test databases from the development 
                               # structure
rake db:test:prepare           # Prepare the test database and load the schema
rake db:test:purge             # Empty the test database

Note 1:

To get rake db:test:* tasks to work with DB2, please refer to the steps described at http://db2onrails.com/ to modify and patch schema_dumper.rb and databases.rake.

Back-up the old files and replace with the two new files supplied in the following directories:

<ruby_path>\lib\ruby\gems\1.8\gems\activerecord-1.15.3\lib\active_record\schema_dumper.rb

<ruby_path>\lib\ruby\gems\1.8\gems\rails-1.2.3\lib\tasks\databases.rake

We will build the tables in our database using the rake db:test:prepare command. Running this command will duplicate the schema from the development database into the test database:










Listing 4. rake db:test:prepare
D:\rails\teamroom> rake db:test:prepare
(in D:\rails\teamroom)

We can now connect to the testdb database and verify that all the tables were successfully created:


Listing 5. Tables in testdb
D:\rails\teamroom>db2 list tables for schema teamroom

Table/View                      Schema          Type  Creation time
------------------------------- --------------- ----- --------------------------
DOCUMENTS                       TEAMROOM        T     2007-06-05-11.21.46.343002
SCHEMA_INFO                     TEAMROOM        T     2007-06-05-11.21.48.306001
SUBJECTS                        TEAMROOM        T     2007-06-05-11.21.46.633003
SUBJECTS_SUBSCRIPTIONS          TEAMROOM        T     2007-06-05-11.21.47.004000
SUBSCRIPTIONS                   TEAMROOM        T     2007-06-05-11.21.47.194005
USERS                           TEAMROOM        T     2007-06-05-11.21.47.595003
XML_CONTENTS                    TEAMROOM        T     2007-06-05-11.21.47.955001

  
  7 record(s) selected.

With all the tables defined in the test database, you can now load test data. Although you can do this manually through SQL inserts or imports, the less tedious (and more manageable) approach would be to make use of text fixtures, which we will talk about shortly.

Ruby's built-in unit tests

Note 2:

The "<" symbol on the class declaration signifies that the class declared on the left side of the symbol is a subclass of the class specified on the right side.

One of the key libraries shipped with Ruby (1.8.1 and higher) is test/unit. To write Ruby test scripts, we'll:

  1. Specify "require 'test/unit'" at the top of the .rb file. This statement will load the Test::Unit module.
  2. After the require statement, a class declaration stating that this class is a subclass of Test::Unit::TestCase must be added (as shown in below in Listing 6).

Listing 6. Specifying "require 'test/unit'" for the unit test
require 'test/unit'

class SubTestCase < Test::Unit::TestCase
  ...
end

In the Team Room application path's test\unit subdirectory, you will find .rb files which correspond to all model objects created through scaffolding. The files will have a "_test" appended to the object name.

When we are creating test scripts under the test\unit subdirectory, the require 'test/unit'statement does not need to be specified. The test_helper.rb file, which is referenced in the existing require statement, already contains the necessary information.


Listing 7. The test\unit path listing
D:\rails\teamroom\test\unit>dir
 Volume in drive D has no label.
 Volume Serial Number is C8D3-5819

 Directory of D:\rails\teamroom\test\unit

06/05/2007  09:27 AM    <DIR>          .
06/05/2007  09:27 AM    <DIR>          ..
06/05/2007  09:27 AM               208 customer_info_test.rb
05/31/2007  07:42 PM               199 document_test.rb
06/02/2007  05:14 PM               251 subjects_subscription_test.rb
05/31/2007  07:42 PM               315 subject_test.rb
05/31/2007  07:42 PM             1,211 subscription_mailer_test.rb
05/31/2007  07:42 PM               207 subscription_test.rb
05/31/2007  07:42 PM               191 user_test.rb
05/31/2007  07:42 PM               204 xml_content_test.rb
               8 File(s)          2,786 bytes
               2 Dir(s)  15,359,959,040 bytes free

Methods with the "test_" prefix

All test method names in Ruby must start with "test" as the first four characters, or the testing framework won't recognize it as a test. Without the "test" prefix, it will be treated as a normal Ruby method and the testing framework won't run it automatically.


Listing 8. A trivial unit test
require File.dirname(__FILE__) + '/../test_helper'

class Sub_Test < Test::Unit::TestCase
  def test_truth
    assert true
  end
end

We can now save the file as sub_test.rb. When executed, it will result in the following output.


Listing 9. Running sub_test.rb
D:\rails\teamroom\test\unit>ruby sub_test.rb
Loaded suite sub_test
Started
.
Finished in 0.631 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

Note 3:

The output from the test above is a ".". In Rails, "." means pass or success, "F" means failure (which is caused by a failed assertion due to unexpected results), and "E" means error (usually a Ruby problem).

The above simple test scenario performed one assert and resulted in a successful scenario due to the "assert true" statement. Assertions are the tests and as you will see in the following section, there are a number of assert statements that you can use for Rails application testing.

In Rails, every model and controller created using the script/generate model, script/generate controller and script/generate scaffold creates corresponding test stubs in the test subdirectory. When using these test stubs, "require 'test/unit'" does not need to be specified.

Assertions

Assertions verify pre-conditions and post conditions for an application or module to ensure a valid state. The Test::Unit assertion follows a basic pattern, whereby the first parameter is the result expected and the second parameter is the actual result. When the expected and actual value don't match, a message is generated explaining what went wrong.

Go to Test::Unit::Assertions for a list of the core Ruby assertions.

There are also a number of available assertions for Rails developers to use. Go to Module Test::Unit::Assertions Information for information regarding this.

Assertions are used to ensure valid states, as previously mentioned. So, what defines valid state?

In most part, a valid state is controlled by the validation in our code.

Using validations

Input data can be validated before it even gets stored in the database. To do this, you can define custom validations or use a set of standard validations. For standard validations like checking the name, length and format of an attribute, we can make use of validations provided by Active Record.

Note 4:

All Active Record validation method names start with "validates_" and take in one or more attribute names and/or one or more validation options.

Adding validation methods to a model causes validation to occur before the object is saved into the database. If validation passes, the object is written to the database. If validation fails, the error message associated with the failing validation method for the object is added to an error list. This list can be displayed to help the user take appropriate action to resolve the failures.

This article will show examples of how to validate a model using Active Record's set of validators.

For our Team Room example, say we wanted the following validations for a subject:

  • All associated document objects to the subject are valid objects
  • The subject name is not empty
  • The subject name is not more than 20 characters in length

To ensure we check for the above cases, we can make use of some standard validation methods. We add the following validations to app\models\subject.rb:


Listing 10. Validations in app\models\subject.rb
  validates_associated      :documents
  validates_associated      :subscriptions				
  validates_presence_of     :name
  validates_length_of       :name,
                            :maximum => 20,   # maximum 20 characters
                            :too_long => "is too long, maximum 20 characters"
  validates_numericality_of :size,            # value is numeric and only integer accepted
                            :only_integer => true,
                            :greater_than => 0                             
  validates_presence_of     :description
  validates_length_of       :tag,
                            :maximum => 10,   # maximum 10 characters
                            :too_long => "is too long, maximum 10 characters"   

The "validates_associated" method will check that all associated documents with the subject are valid. The "validates_presence_of" method will check that the subject name is not empty.

To check that the subject name length is not more than 20 characters, we use the "validates_length_of" method. We use the "maximum" configuration option to indicate the maximum size of the subject name. The "too_long" option indicates the error message that is generated if the subject name is over 20 characters in length, and is used in place of the "message" option. By default, the "too_long" message is "is too long (maximum is %d characters)".

Note 5:

The validates_size_of method is an alias to the validates_length_of method

Subject input data will now be validated according to the rules set above. If any user enters problematic data, we want to ensure that the list of error messages is displayed so that the user can correct the input data.

There are ActionView methods that can help:

  • error_message_on - returns the error string for the specified attribute of the object
  • error_messages_for - returns an error list for the specified object(s)

Find more information on the methods here.

In our Team Room example, if we want to list all input data error messages entered for a new subject, then we can add the following to the top of app\views\subjects\_form.rhtml.


Listing 11. Input data error messages
<%= error_messages_for 'subject' %>

The method call will return a div with all error messages (if any exist), so that one can be easily displayed:


Figure 1. Error message when entering a subject name that is too long
Error message

You can find a list of the Active Record model validations that can be defined in the class scope here.

Now that we have the subject model validation in place, we can actually test the above validation using an assertion.

Using assertions

We can now utilize assertions to test models associated with our SUBJECTS table. One of the models associated with the SUBJECTS table ensures that the table is populated with necessary information with valid lengths. These validations are important to avoid putting an unnecessary load on the backend database. The validations which will be tested are outlined in \app\models\subject.rb (please refer to Listing 10).

The "test_invalid_with_empty_values" method is a simple example, which demonstrate asserting error conditions with empty values. In the "test_invalid_with_empty_values" method, we test whether empty subject values result in error.


Listing 12. Test method "test_invalid_with_empty_values"
require File.dirname(__FILE__) + '/../test_helper'

class SubjectTest < Test::Unit::TestCase

# Tests validation failure using empty values
  def test_invalid_with_empty_values
    subject = Subject.new
    assert !subject.valid?
    assert subject.errors.invalid?(:name)
    assert subject.errors.invalid?(:size)
    assert subject.errors.invalid?(:description)
  end
  ...

Now, let's add a second test to assert valid subject data and a third test to see whether the expected error message is encountered when we specify an incorrect TAG length.

The following three test methods should now be present in test/unit/subject_test.rb:


Listing 13. Test methods for subject_test.rb
require File.dirname(__FILE__) + '/../test_helper'

class SubjectTest < Test::Unit::TestCase

# Tests validation failure using empty values
  def test_invalid_with_empty_values
    subject = Subject.new
    assert !subject.valid?
    assert subject.errors.invalid?(:name)
    assert subject.errors.invalid?(:size)
    assert subject.errors.invalid?(:description)
  end
  
# Tests validation using valid data
  def test_valid_subject
    subject = Subject.new(:name=>"Perl",
                          :size=>2,
                          :description=>"test",
                          :tag=>"123456789")
    assert subject.valid?
  end

# Tests invalid TAG length  
  def test_invalid_tag_length
    subject = Subject.new(:name=>"AJAX",
                          :size=>3,
                          :description=>"test",
                          :tag=>"abcdefghijklm")
    assert !subject.valid?
    assert_equal "is too long, maximum 10 characters",subject.errors.on(:tag)
  end 
end

The successful outcome of the above unit test should result in following output:


Listing 14. Running a subject unit test
D:\rails\teamroom>ruby test/unit/subject_test.rb
Loaded suite subject_test
Started
...
Finished in 13.891 seconds.

3 tests, 7 assertions, 0 failures, 0 errors

Note 6:

SubjectTest is a TestCase, which is a class inherited from Test::Unit::TestCase and can contain one or more tests. A collection of test cases are referred to as a TestSuite. A TestCase contains many assertions.

All three tests in the output refer to three test methods defined in subject_test.rb file, and seven assertions refer to all the assert statements in the file. As previously mentioned in Note 3, "." refers to successful result.

The above subject_test.rb example contains seven assertions as outlined in Listing 15 below.


Listing 15. Assertions in subject_test.rb
assert !subject.valid?
assert subject.errors.invalid?(:name)
assert subject.errors.invalid?(:size)
assert subject.errors.invalid?(:description)

assert subject.valid?

assert !subject.valid?
assert_equal "is too long, maximum 10 characters",subject.errors.on(:tag)

And there are three tests in subject_test.rb as shown in Listing 16.


Listing 16. Tests methods in subject_test.rb
test_invalid_with_empty_values
test_valid_subject
test_invalid_tag_length

Testing with fixtures

Instead of manually entering values in the browser to test your Rails application, you can use a test fixture to load sample data into your database tables. Fixtures allow you to populate your test database with predefined data values before you run your tests. Fixtures can be in two different formats: YAML or comma-separated value (CSV) format. Since fixtures are database independent, you can use the same fixture to test your Rails application against different database systems.

Note 7:

The name of the fixture file must match the underlying table name in the database. So, to load data into the SUBJECTS table in our Team Room, the subject fixture file must be named subjects.yml.

We'll use fixtures to test subjects and subscriptions in our Team Room application. For our test, we prefer to use a YAML fixture. To do this, we edit the subjects.yml file in the /test/fixtures directory. An empty subjects.yml file is created by Rails when it generates the corresponding subject unit test. When you run ruby script/generate model to create a new model, or ruby script/generate scaffold to generate a scaffold, fixture stubs will be automatically created and placed in the /test/fixtures directory.

Note 8:

Each YAML fixture file contains the data for a single model.

We edited subjects.yml and populated it with 24 different subjects. You can find /test/fixtures/subject.yml in the sample code provided in the Downloads section of this article. Each fixture has a name, followed by a list of colon-separated key-value pairs.

Sample contents of subjects.yml:


Listing 17. Sample contents of subjects.yml
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
photography:
  id: 1
  name: Photography
  size: 0
  description: Digital Photos, images
  tag: portraits

gardening:
  id: 2
  name: Gardening
  size: 0
  description: Gardening info
  tag: flowers

golf:   
  id: 3
  name: Golf
  size: 0
  description: Golf information, golf club, golf course discount available to members
  tag: golf

tennis:
  id: 4
  name: Tennis
  size: 0
  description: Tennis information, tennis club, tennis court bookings
  tag: tennis 

<etc ..>

The Rails fixture loading mechanism is already in place so you do not have to add additional code to tell Rails to load the test data. We modified /test/unit/subject_test.rb from Listing 13 and added additional code to test with fixtures.


Listing 18. Fixture-related entries in/test/unit/subject_test.rb
require File.dirname(__FILE__) + '/../test_helper'

class SubjectTest < Test::Unit::TestCase
  fixtures :subjects

  # count the number of subject fixtures
  def test_subject_fixtures
    assert_equal 24, Subject.count
  end
  ...  
  # Previous test methods and assertions are listed below
  ...
end

The fixtures method automatically loads the fixture corresponding to the given model name at the start of each test method in the test case. By default, :subjects tells Rails to load the sample data from the subjects.yml file. We then added an additional "test_subject_fixtures" method to test that all 24 subjects are loaded correctly.

To run the test, issue ruby test/unit/subject_test.rb. The output is shown below:


Listing 19. Subject unit test output using fixtures
D:\rails\teamroom>ruby test/unit/subject_test.rb
Loaded suite test/unit/subject_test
Started
....
Finished in 3.15 seconds.

4 tests, 8 assertions, 0 failures, 0 errors

Assuming you have started the WEBrick server and logged in as a registered user, now browse to http://localhost:3000/subjects/list. You will notice that 24 subjects are now available for subscription.


Figure 2. Screenshot of populated subject entries
Subject entries

Sometimes you may also need to use dynamic fixtures with ERb (embedded Ruby) to generate fixture data. One very common example is to generate the actual timestamp as you run your tests. Using <% ... %> allows you to execute Ruby code, and using <%= ... %> allows you to execute Ruby code as well as displaying the results. You will see how to use dynamic fixtures and ERb when we show you how to perform functional tests and performance tests in the upcoming sections.

Sharing fixtures and testing many-to-many associations

We can go one step further and use fixtures to test the associations between subjects and subscriptions. Of course, you can browse to http://localhost:3000/subjects/list and check off a number of subjects to which you want to subscribe as the current logged-in user, and then test the Team Room subject-subscription association. However, this task can become quite tedious if you want to test many user subscriptions that involve a large number of subjects. This would mean you have to manually create a number of user subscriptions, then log in as each user, and check off the subjects to which each user wants to subscribe.

The more efficient way to test a many-to-many relationship between models is to create a new fixture that contains data in the join table. As you may recall in Part 2, we created the SUBJECTS_SUBSCRIPTIONS table to represent the many-to-many relationship between the SUBJECTS and SUBSCRIPTIONS table, and to ensure that the database is normalized. Let's edit the /test/fixtures/subjects_subscriptions.yml file to populate data into the SUBJECTS_SUBSCRIPTIONS table:


Listing 20. subjects_subscriptions.yml
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
# Refer to /test/fixtures/subjects.yml for more information
# Subject ID 2 = Gardening
subscription160_gardening:
  subscription_id: 160
  subject_id: 2
 
# Subject ID 3 = Golf  
subscription160_golf:
  subscription_id: 160
  subject_id: 3

The above means that the user whose subscription ID is 160 has subscribed to two subjects: Gardening and Golf.

Testing the subject and subscription association means we must access both the data in the SUBJECTS and the SUBSCRIPTIONS table, as well as the join table SUBJECTS_SUBCRIPTIONS. Therefore, in the /test/unit/subjects_subscription_test.rb test, we want to load all three fixtures.


Listing 21. Fixtures
class SubjectsSubscriptionTest < Test::Unit::TestCase
   fixtures :subjects_subscriptions, :subjects, :subscriptions
   [...]
end   

This shows that fixtures are very powerful when you need to share test data across multiple test cases. For more information about fixtures, see Class Fixtures in the Ruby on Rails documentation.

Using mock objects

Mocks are useful when you want to test the core functionality of the application without worrying about accessing external systems. Although we do verify that the email address is in the correct format when a user registered with the Team Room, we have no idea whether the email address provided in fact exists. The only way for us to confirm that the user email is in fact valid would be to send an email to the supplied email address. If the email address is not valid or does not exist, we expect to get an error from the email provider. To test this failed subscription notification delivery scenario, we can use a mock.

Note 9:

The mock file must have the exact same name as the model in the /app/models directory. You only redefine methods you want to mock out in the mock file, so you do not have to define all existing methods in the corresponding mock file.

For example, we can mock out the email delivery method in our testing environment. We create an email_provider.rb file in the /test/mocks/test directory that defines the delivery method we want to mock out. With a mock file in place, Rails will load the /test/mocks/test/email_provider.rb first, and then look in /app/models/email_provider.rb.

The /test/mocks/test/email_provider.rb will look like this:


Listing 22. Fixtures
require 'models/email_provider'
 
class EmailProvider
  def deliver(request)
    #Mock method
    :success
  end
end

Mocks allow us to concentrate testing on the core functionality in our Team Room application without worrying about inaccessible external resources.

Functional tests: testing controllers

Testing the Rails application controllers aims to ensure all requests coming from the Web browser are answered as expected, based on the user input, while producing the appropriate state changes. So for each action in a controller, there needs to be at least one test case which would exercise that specific path (URL), and sometimes more than one test can account for different states (for instance, accessing an action while the user is authenticated or not).

For a taste of functional testing specifics, we consider the case of the UsersController involved in the login and registration actions. An associated UsersControllerTest was generated along with the model and controller classes, and it already contains a complete template for setup and also stub methods that only expect minimal customization to handle the execution paths.


Listing 23. Users controller test
require File.dirname(__FILE__) + '/../test_helper'
require 'users_controller'

# Re-raise errors caught by the controller.
class UsersController; def rescue_action(e) raise e end; end

class UsersControllerTest < Test::Unit::TestCase
  fixtures :users

  def setup
    @controller = UsersController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new

    @first_id = users(:first).id
  end
  [...]
end

An essential method, the setup initializes the three instance variable always required for the functional test: the controller to be tested, the request containing the user input along with HTTP header information, and the response packing the data rendered from templates along with status codes.

A key aspect of testing the controller is the associated fixture providing the test data for our scenario, and this specific controller gives us a chance to understand the usage of some dynamic aspects in fixtures. In the test case above, featuring an authenticated user, there is a need to populate the USERS table with a valid entry to compute the encrypted password. Although the encrypted password could be calculated separately and just used directly in the fixtures, the template substitution used in views and partials works similarly, simplifying our task.


Listing 24. Users fixture
<% SALT = "tstsalt" unless defined?(SALT) >
first:
  id: 141
  usertype:  usr
  firstname: Homer
  lastname:  Simpson
  extension: '9955'
  email:     homer@simpson.org
  userid:    homers
  salt: <%= SALT %>
  hash_passwd: <%= User.encrypt('secret' , SALT) %> 

The initial test_index test case would need to be split into two different test cases that would allow a more precise verification of request handling when the user is authenticated, as opposed to not being authenticated:


Listing 25. Test index action in Users controller
  def test_index_without_user
    get :index
    assert_redirected_to :action => "login"
    assert_equal "Please sign-in" , flash[:notice]
  end

  def test_index_with_user
    get :index, {}, {:user_id => users(:first).id}
    assert_response 302
  end

To exercise the index action for this controller, which would invariably end up on the login page if the user is not yet authenticated, the test case needs to simulate the GET request from the browser. To generate an HTTP request, the get method defined by the ActionController::Integration::Session takes the URL, a hash representing HTTP parameters passed with the action (empty in the case above), and a hash of parameters to populate the session. Now we just need to invoke the specific test case:


Listing 26. Run users controller test
D:\rails\teamroom>ruby test\functional\users_controller_test.rb -n test_index_with_user
Loaded suite test/functional/users_controller_test
Started
.
Finished in 0.719 seconds.
1 tests, 1 assertions, 0 failures, 0 errors

Testing email notification and subscription confirmation

Testing email notification involves both unit testing and ensuring the generated email content verification, but also functional testing which will make sure the email notification is always sent out when expected. Based on the email notification controller implemented in Part 2 based on the Action Mailer, we should locate the stub functional test generated SubscriptionMailerTest and take advantage of the fixtures already created for the models involved in the notification action.

For unit testing email notification and subscription confirmation, the mailer test drive requires you to configure ActionMailer for local email delivery (delivery_method = :test), and also to use the model fixtures to setup the objects required to call the notification and confirmation.


Listing 27. Mailer functional test setup
class SubscriptionMailerTest < Test::Unit::TestCase
  FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures'
  CHARSET = "utf-8"

  include ActionMailer::Quoting
  fixtures :users
  fixtures :subscriptions
  fixtures :subjects
  fixtures :documents

  def setup
    ActionMailer::Base.delivery_method = :test
    ActionMailer::Base.perform_deliveries = true
    ActionMailer::Base.deliveries = []

    @user = users(:first)
    @subscription = subscriptions(:one)
    @doc  = documents(:first)
    @subject = subjects(:ruby_on_rails)
    @subject.documents << @doc
    @subject.subscriptions << @subscription
  end
  [...]
end

The test implementation then uses the mailer class to create the email content (create_notify), but not actually to send to an SMTP server, and through the usage of assertions verifies the generated parts of the message (for instance, recipients, sender, timestamp, and so on) including certain sections of the body.


Listing 28. Notification email test
  def test_notify
    response = SubscriptionMailer.create_notify(@doc)

    assert_equal("TeamRoom new shared document notification", response.subject)
    assert_equal("teamroom@developerWorks.ibm.com", response.from[0])
    assert_equal(nil, response.to)
    assert_equal("homer@simpson.org", response.bcc[0])
    assert_in_delta(Time.now, response.date, 1.0)
    assert_match(/A new document DB2onRails-logo.gif/, response.body)
    assert_match(/subject: Ruby on Rails/, response.body)
  end

A similar approach is being used to test the subscription confirmation email while creating and then verifying the content of the message. Furthermore, the functional testing of the emailer implies a controller test case which will not deliver emails, but rather append email messages to an in-memory array (ActionMailer::Base.deliveries = []). This allows for verifying the number of emails sent out, and also for validating the content of each message.

Performance test

Although such a template is not generated by default in the list of test directories outside of unit, functional and integration, developers could be interested in estimating some application performance characteristics. While focusing on performance too early in the development is never wise, by the time the development process reaches the end cycles, some performance scenarios are recommended to address capacity planning and to ensure some regression testing when things start to get slow in subsequent iterations.

For a glimpse into how performance testing can be implemented along the other test categories, let's consider a scenario that might amount to an excessive load on the Team Room application if deployed in a relatively large enterprise environment. We start as before with some test data, and for that we will provide a set of fixtures that only get loaded for performance testing, to localize when a relatively large amount of test date is initialized.


Listing 29. Another dynamic user fixture for large data test set
<% SALT = "tstsalt" unless defined?(SALT) %>
<% 200.upto(1200) do |i| %>user_<%= i %>:
  id: <%= i %>
  usertype:  usr
  firstname: New<%= i %>
  lastname:  One
  extension: '9999'
  userid:    new<%= i %>
  email:     new<%= i %>@domain.net
  salt:      <%= SALT %>
  hash_passwd: <%= User.encrypt('secret' , SALT) %>
<% end %>

We can consider the performance test to be based on a regular usage scenario, and extend to the large data test set imposing some extreme loads. That implies the test controller is fairly similar to that used for regular functional testing, but the test case is exaggerating a certain aspect. Let's consider the user controller and the login action to be tested on extreme loads. Except from where the test data is loaded (self.fixture_path), the test case setups should be identical.


Listing 30. User login performance test setup
class UserLoginTest < Test::Unit::TestCase
  self.fixture_path = Pathname.new(File.dirname(__FILE__)).parent + 
                                   'fixtures' + 'performance'
  fixtures :users

  def setup
    @controller = UsersController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
  end
  [...]
end

Now, to test the Team Room capabilities in handling a large number of simultaneous users, we can attempt to login all users defined in the fixture at once, and maybe even impose some minimum time limits for these actions. In Listing 31, we have implemented that in a loop, while making sure the logger is turned off, in order to collect time estimates using the Ruby standard library benchmark:


Listing 31. Test user controller performance during login action
  def test_1000_login
    @controller.logger.silence do
      user_count = 0
      elapsed_time = Benchmark.realtime do
        1200.downto(200) do |id|
          user = users("user_#{id}")
          post :login, :userid => user.userid, :password => 'secret'
          user_count += 1
        end
      end
      assert_equal 1001, user_count
      assert elapsed_time < 10.0
    end
  end

A more realistic performance scenario might involve some of the XML queries that stand behind the marketing reports. We shall leave that as an exercise for readers interested in moving this or other similar applications to production. An important aspect of performance testing implementation is the usage of a regular functional and integration test case in connection with large test data sets and extensive loads. This approach makes the implementation of such performance regression harnesses quite easy and very effective.

Running application tests using Rake

When we started to build the Team Room application using the rails teamroom command, a directory structure was generated, and even more importantly, a set of utilities built into the Rails gem were "hooked". The Rails gem is quite a thin and simple component that orchestrates behind the scenes the seamless integration of all other components; two of the most visible being Active Record and Action View. At the same time, the Rails gem addresses a set of common concerns across all Web applications using the framework through the powerful Rake gem helper.

Rake, the Ruby equivalent of the make utility, through its predefined task (the equivalent of make targets) not only ensures simple shortcuts to migration, schema load/dump, Rails gems update/freeze, and documentation creation, but also for testing. Running rake --tasks in the Team Room application root directory, one can get acquainted with the entire set of predefined tasks provided by the reliable assistant, rake. It all gets infused into the Team Room application from the very beginning, when the directory structure is created and the Rake file is created, the tasks/rails library path containing the set of predefined tasks is then shared.

With a simple rake test command, all unit and functional tests built so far can be executed together, and can easily be invoked during the development cycle. The tests can also be run based on category, and the Rails application standard directory structure makes it easy to load all defined test in the test/unit path as in Listing 32:


Listing 32. Running all unit tests for Team Room application
D:\rails\teamroom>rake test:units
(in D:/rails/teamroom)
D:/ruby/bin/ruby -Ilib;test 
"D:/ruby/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake/rake_test_loader.rb" 
"test/unit/customer_info_test.rb" "test/unit/document_test.rb" 
"test/unit/subjects_subscription_test.rb" "test/unit/subject_test.rb" 
"test/unit/subscription_mailer_test.rb" "test/unit/subscription_test.rb" 
"test/unit/user_test.rb" "test/unit/xml_content_test.rb"
Loaded suite D:/ruby/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake/rake_test_loader
Started
..........
Finished in 11.553 seconds.

The same generic test loading occurs while calling rake test:functionals, while rake chains such test execution, to cover the entire set.

DB2 Rails application problem determination

As you develop your Rails application, you may observe unexpected results or errors. The first thing to check are the log files in the /log directory (in our example, d:\rails\teamroom\log). Rails writes logs and errors for each environment in its own log. For problems in the development environment, look at development.log, for problems in the test environment, look at test.log. In addition to the Rails log files and diagnosing problems in the application itself, DB2 trace utilities and logs can shed further light in diagnosing Rails application problems.

CLI trace

As mentioned in Part 1 of our series, the ibm_db driver utilizes the IBM driver for Open Database Connectivity (ODBC) and Call Level Interface (CLI) to connect to IBM data servers. This means that all interactions with IBM data servers will go through IBM driver for ODBC and CLI, and can be captured using the CLI trace. A CLI trace captures all of the API calls made by the Ruby application to the DB2 driver for ODBC and the CLI driver, and logs all input parameters as well as values returned from the driver to the application. Therefore, the CLI trace provides valuable insight into input and output values as well as actual SQL statements generated by the Rails framework that occur behind the scene.

Steps for enabling a CLI trace can be found at the Infocenter.

Note 10:

As a CLI trace is written to a disk, it will have an impact on application performance. Also, a CLI trace is not dynamic as it requires the application or Web server in which the application is running to be restarted for the trace (or any other CLI setting in db2cli.ini) to take effect.

A CLI trace is a valuable tool to verify correct SQL statements are being issued by the application and correct values are returned from the database. As most of the SQL statements are generated by Rails in our Team Room application, following CLI trace output shows the query sent to the DB2 database by uploading the XML document as shown in Figure 2. Uploading the XML document involves inserting data into three separate tables.

  1. Inserting the content type, name, platform, size, subject and id of the user who uploaded the file as well as updated and created time to documents table.



    Listing 33. CLI trace: Part 1
    SQLExecDirect( hStmt=1:8 )
        ---> Time elapsed - +1.844000E-003 seconds
    ( pszSqlStr="INSERT INTO documents (content_type, name, platform, size, updated_at, 
    subject_id, user_id, created_at, data) VALUES('text/xml', 'CAN-Central.xml', 'Any', 
    125177, '2007-06-07 13:12:57', NULL, 100, '2007-06-07 13:12:49', NULL)", cbSqlStr=225 )
    ( StmtOut="INSERT INTO documents (content_type, name, platform, size, updated_at, 
    subject_id, user_id, created_at, data) VALUES(?, ?, ?, 125177, ?, NULL, 100, ?, NULL)" )
    ( Package="SYSSH200          ", Section=11 )
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 349 )
        sqlccsend( ) rc - 0, time elapsed - +1.991000E-003
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 188 ) - rc - 0, time elapsed - +2.471000E-003
    ( Row=1, iPar=1, fCType=SQL_C_CHAR, rgbValue="text/xml" - x'746578742F786D6C', pcbValu...
    ( Row=1, iPar=2, fCType=SQL_C_CHAR, rgbValue="CAN-Central.xml" - x'43414E2D43656E74726...
    ( Row=1, iPar=3, fCType=SQL_C_CHAR, rgbValue="Any" - x'416E79', pcbValue=3, piIndicato...
    ( Row=1, iPar=4, fCType=SQL_C_CHAR, rgbValue="2007-06-07 13:12:57" - x'323030372D30362...
    ( Row=1, iPar=5, fCType=SQL_C_CHAR, rgbValue="2007-06-07 13:12:49" - x'323030372D30362...
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 223 )
        sqlccsend( ) rc - 0, time elapsed - +2.380000E-004
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 127 ) - rc - 0, time elapsed - +1.909000E-003
    
    SQLExecDirect( )
        <--- SQL_SUCCESS   Time elapsed - +1.504370E-001 seconds
    ...
    

  2. Inserting the name of the XML file, corresponding id from documents table and XML placeholder.



    Listing 34. CLI trace: Part 2
    SQLExecDirect( hStmt=1:8 )
        ---> Time elapsed - +5.003000E-003 seconds
    ( pszSqlStr="INSERT INTO xml_contents (name, document_id, data) VALUES('CAN-Central.xml',
     101, '<ibm>@@@IBMXML@@@</ibm>')", cbSqlStr=108 )
    ( StmtOut="INSERT INTO xml_contents (name, document_id, data) VALUES(?, 101, ?)" )
    ( Package="SYSSH200          ", Section=11 )
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 261 )
        sqlccsend( ) rc - 0, time elapsed - +1.799000E-003
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 137 ) - rc - 0, time elapsed - +1.865000E-003
    ( Row=1, iPar=1, fCType=SQL_C_CHAR, rgbValue="CAN-Central.xml" - x'43414E2D43656E747261...
    ( Row=1, iPar=2, fCType=SQL_C_CHAR, rgbValue="<ibm>@@@IBMXML@@@</ibm>" - x'3C69626D3E40...
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 221 )
        sqlccsend( ) rc - 0, time elapsed - +1.000000E-005
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 89 ) - rc - 0, time elapsed - +1.840000E-003
    
    SQLExecDirect( )
        <--- SQL_SUCCESS   Time elapsed - +8.456800E-002 seconds
    ...
    

  3. This is followed by an update statement to actually insert XML data.



    Listing 35. CLI trace: Part 3
    SQLPrepare( hStmt=1:8 )
        ---> Time elapsed - +1.861000E-003 seconds
    ( pszSqlStr="UPDATE xml_contents SET (data) = (?) WHERE id = 101", cbSqlStr=51 )
    ( StmtOut="UPDATE xml_contents SET (data) = (?) WHERE id = 101" )
    ( Package="SYSSH200          ", Section=11 )
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 244 )
        sqlccsend( ) rc - 0, time elapsed - +1.000000E-005
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 120 ) - rc - 0, time elapsed - +1.923000E-003
    
    SQLPrepare( )
        <--- SQL_SUCCESS   Time elapsed - +3.831300E-002 seconds
    ...
    SQLDescribeParam( hStmt=1:8, usPar=1, psSQLType= ...
        ---> Time elapsed - +2.315000E-003 seconds
    
    SQLDescribeParam( psSQLType=SQL_XML, puiParamDef=0, psScale=0, ...
        <--- SQL_SUCCESS   Time elapsed - +2.457500E-002 seconds
    
    SQLBindParameter( hStmt=1:8, iPar=1, fParamType=SQL_PARAM_INPUT, fCType=SQL_C_BINARY, 
    fSQLType=SQL_XML, cbColDef=0, ibScale=0, rgbValue= ...
        ---> Time elapsed - +2.225000E-003 seconds
    
    SQLBindParameter( )
        <--- SQL_SUCCESS   Time elapsed - +3.743500E-002 seconds
    
    SQLExecute( hStmt=1:8 )
        ---> Time elapsed - +1.848000E-003 seconds
    E0D0A202020202020202020203C2F6974656D3E0D0A202020....., pcbValue=125177 )
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 61440 )
        sqlccsend( ) rc - 0, time elapsed - +2.710000E-004
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 61440 )
        sqlccsend( ) rc - 0, time elapsed - +2.120000E-004
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 2450 )
        sqlccsend( ) rc - 0, time elapsed - +1.300000E-005
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 89 ) - rc - 0, time elapsed - +2.345000E-003
    
    SQLExecute( )
        <--- SQL_SUCCESS   Time elapsed - +9.107600E-002 seconds
    ...
    

  4. Lastly, the SUBJECTS table is populated accordingly, using an insert statement.



    Listing 36. CLI trace: Part 4
    SQLExecDirect( hStmt=1:10 )
        ---> Time elapsed - +2.166000E-003 seconds
    ( pszSqlStr="INSERT INTO subjects (name, size, tag, description) VALUES('Marketing', 1, 
    'sales', '@@@IBMTEXT@@@')", cbSqlStr=100 )
    ( StmtOut="INSERT INTO subjects (name, size, tag, description) VALUES(?, 1, ?, ?)" )
    ( Package="SYSSH200          ", Section=14 )
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 263 )
        sqlccsend( ) rc - 0, time elapsed - +1.920000E-004
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 154 ) - rc - 0, time elapsed - +1.823000E-003
    ( Row=1, iPar=1, fCType=SQL_C_CHAR, rgbValue="Marketing" - x'4D61726B6574696E67', ...
    ( Row=1, iPar=2, fCType=SQL_C_CHAR, rgbValue="sales" - x'73616C6573', pcbValue=5, ...
    ( Row=1, iPar=3, fCType=SQL_C_CHAR, rgbValue="@@@IBMTEXT@@@" - x'40404049424D5445 ...
        sqlccsend( Handle - 84681200 )
        sqlccsend( ulBytes - 182 )   
        sqlccsend( ) rc - 0, time elapsed - +3.160000E-004
        sqlccrecv( timeout - +0.000000E+000 )
        sqlccrecv( ulBytes - 89 ) - rc - 0, time elapsed - +2.128700E-002
    
    SQLExecDirect( )
        <--- SQL_SUCCESS   Time elapsed - +1.243580E-001 seconds
    

DB2 trace

A DB2 trace captures all traceable internal DB2 function calls and are an invaluable tool to investigate application problems involving DB2. It provides information regarding DB2 internal activities at the time of application execution. There are several characteristics of a DB2 trace which allow for effective information gathering.

  1. The trace can be turned on and off dynamically. The trace can be enabled and disabled without recycling the application. This means that if the exact point of failure is known, the trace can be enabled just prior to the incident to gather only relevant information.
  2. DB2 trace information can be stored in memory or disk.
  3. You can apply trace mask to trace out only specific components.

For instructions on enabling a DB2 trace, please refer to the Infocenter.

When troubleshooting a DB2 issue with Rails applications, it is useful to gather a DB2 trace with a CLI trace (together) on the client where application resides.

The db2diag.log and db2diag tool

The db2diag.log logs errors and some warnings encountered by DB2. The db2diag.log can be reviewed to determine if any DB2 errors or warnings were logged during execution of our application. The sensitivity of the log can be changed from the default of 3 to other values from dbm cfg.

The db2diag analysis tool is available for DB2 9® to filter and format the db2diag.log file. It allows for filtering log entries that pertain to specific database or timestamp values among others. For further details regarding the db2diag tool, please refer to the Infocenter.

Frequently asked questions and caveats

  1. Is a DB2 client installation required in order to use the ibm_db Ruby adapter and driver? Yes, as stated in Part 1 of the DB2 and Ruby on Rails series, the IBM_DB adapter (ibm_db_adapter.rb) has a direct dependency on the ibm_db Ruby driver, which utilizes the IBM driver for ODBC and the CLI to connect to IBM data servers. Therefore, the minimal requirement is the IBM DB2 Driver for ODBC and CLI, but any DB2 9 FP2 or higher client package (which includes the CLI driver) would be sufficient to enable the IBM_DB adapter connection to all supported IBM data servers.
  2. The following error is observed when trying to run a Rails application against DB2. SQL0954C: Not enough storage is available in the application heap to process the statement.As stated in Part 1, Rails applications on DB2 9 require a minimum APPLHEAPSZ of 1024. To check APPLHEAPSZ, connect to your specific database and retrieve the configuration parameters.
  3. Is DB2 Connect required to access a DB2 i5 or a DB2 for z/OS server by way of IBM Ruby Driver? Yes, DB2 Connect is required in order for a DB2 client to connect to DB2 i5 or DB2 z/OS server. When using the IBM DB2 driver for ODBC and CLI, a valid license file is required in the driver install path.
  4. Getting rake aborted errors when trying to clone a test environment with development using rake db:test:* commands. Check database.yml file. The test environment needs to be setup according to database.yml specifics as provided in the default configuration generated by the rails command. Also, make sure you obtain the two fixes as stated in Note 1.
  5. Encountering problems that are known to be fixed in the latest version of the IBM_DB adapter. However, the gem list --local command shows that the latest version of the IBM_DB adapter is installed. Check to make sure you do not have a copy of ibm_db_adapter.rb in <ruby_path>\lib\ruby\gems\1.8\gems\activerecord-1.15.3\lib\active_record\connection_adapters or a similar path on UNIX®. There should only be one copy of ibm_db_adapter.rb in the latest IBM_DB gem installed in the GEM_HOME path <ruby_path>\lib\ruby\gems\1.8\gems\ibm_db-<version>-mswin32\lib\active_record\connection_adapters (or a similar path on UNIX). This is the only IBM_DB adapter that is loaded in the Rails environment.
  6. For the upcoming Rails 1.2.4, config.connection_adapters and RAILS_CONNECTION_ADAPTERS will be eliminated. Therefore, there will be no further need to register "ibm_db" in the list of connection adapters in the Rails framework through the manual step described in Part 1. Once the IBM_DB adapter is installed using the gem install ibm_db command, it will be immediately available for the Rails environment to find and load.

Conclusion

The built-in test support in the Rails framework makes testing easy! Each Rails application has a test, development and production environment defined in the config/database.yml file that allows you to set up different databases for various purposes. When you initially create a new Rails project, Rails generates a test infrastructure for you. For every model and controller you create, corresponding test stubs are created. Unit tests test the Rails models, while functional and integration tests ensure the Rails application works as designed at a higher level. Fixtures allow you to specify data for testing, and finally, mock objects let you concentrate on testing the core application without worrying about accessing network connections or external systems. These features are built in to Rails, making it exceptionally convenient to test.



Download

DescriptionNameSizeDownload method
Team room sample codeTeamroom3.zip10KB HTTP

Information about download methods


Resources

Learn

Get products and technologies

Discuss

About the authors

John Chun is a specialist of the DB2 Advanced Support team working in the area of application development and tooling. He has worked in the IBM DBT Toronto lab for 7 years resolving DB2 application issues with various languages including Java, C, C++, Perl, REXX, C# and others. John has worked on a number of projects involving the DB2 CLI and OLEDB driver, as well as the .NET data provider. John is a DB2 Certified Solutions Expert and Certified Websphere Administrator.

Alex Pitigoi is an advisory software engineer at the IBM Toronto Lab. He has worked on various software development projects in the Information Management since 1998, focusing on Web technologies and database administration. Most recently, he drove the development of the SQLModel project, now incorporated into the Eclipse Data Tools Project, as well as the overall architecture for the database administrative Web Tools across multiple IBM data servers. Alex also worked on the DB2 Satellite Administration Center, the IBM Express Runtime, and lead the development of the first set of Web Tools delivered for DB2. His current focus is IBM's data servers enablement for new open source technologies (Ruby, Python, PHP).

Christine Law is senior DB2 specialist and an IBM Certified Expert at the IBM Toronto Lab, where she is responsible for resolving DB2 applications problems and defects. She has extensive application development experience on Linux, UNIX and Windows platforms with different programming languages and scripting languages, specializing in JDBC, SQLJ, stored procedures and embedded SQL. Her recent interests includes Open Source technologies such as AJAX and Ruby.

Naomi Ngan completed an Honours degree in Computer Science and Statistics at the University of Toronto in Canada in 2000. Upon graduating, she joined IBM, where she was responsible for resolving IBM DB2 RDBMS product defects and problems in an application development environment. After almost 4 years at IBM, she went on to the Ernest Gallo Clinic and Research Center at UCSF to develop Bioinformatics software. This includes designing and developing database objects, JSPs, Java standalone applications and stored procedures within an XML environment on Linux and Windows platforms. Currently, she is a senior software engineer at Autonomy Corporation developing J2EE enterprise software. She has in-depth knowledge of application development and tooling for DB2 and holds numerous IBM and Sun developer certifications in DB2, XML, WebSphere, and Java/J2EE.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Information Management, Open source
ArticleID=232475
ArticleTitle=DB2 and Ruby on Rails, Part 3: Testing with DB2 and Ruby on Rails
publish-date=06212007
author1-email=chunj@ca.ibm.com
author1-email-cc=
author2-email=apitigoi@ca.ibm.com
author2-email-cc=
author3-email=cwylaw@ca.ibm.com
author3-email-cc=
author4-email=naomin@gmail.com
author4-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers