plsql_unit_test 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +153 -0
- data/Rakefile.rb +30 -0
- data/lib/plsql_unit_test/setup.rb +8 -0
- data/lib/plsql_unit_test/test_unit_patch.rb +40 -1
- metadata +7 -6
data/README.md
CHANGED
@@ -0,0 +1,153 @@
|
|
1
|
+
# PLSQL Unit Test
|
2
|
+
|
3
|
+
This gem is intended to be used to test PLSQL code. Most of the test framework is provided by Test::Unit, and this gem simply adds a few convenience methods that are useful when unit testing PLSQL code.
|
4
|
+
|
5
|
+
# Dependencies
|
6
|
+
|
7
|
+
This gem depends on:
|
8
|
+
|
9
|
+
* [SimpleOracleJDBC](http://rubygems.org/gems/simpleOracleJDBC) to provide a JDBC interface to Oracle
|
10
|
+
* [Data_Factory](http://rubygems.org/gems/data_factory) to provide an mechanism for generating test data
|
11
|
+
* The Oracle ojdbc6.jar drivers should be in the jruby lib directory
|
12
|
+
|
13
|
+
The gem also requires JRuby, mostly because SimpleOracleJDBC requires JRuby. It would be possible to make it run in MRI Ruby, but a different database interface would be required, using perhaps OCI8.
|
14
|
+
|
15
|
+
# How to Create a PLSQL Unit Test Suite
|
16
|
+
|
17
|
+
## Test Helper
|
18
|
+
|
19
|
+
First, create a new directory for the tests. Then create a file called test_helper.rb, it should contain something like the following:
|
20
|
+
|
21
|
+
require 'rubygems'
|
22
|
+
require 'plsql_unit_test'
|
23
|
+
|
24
|
+
PLSQLUnitTest::Setup.connect('sodonnel', # user
|
25
|
+
'sodonnell', # password
|
26
|
+
'local11gr2.world', # service
|
27
|
+
'localhost', # IP address
|
28
|
+
'1521') # port
|
29
|
+
|
30
|
+
This code does two things. It loads the plsql\_unit\_test gem (which also requires SimpleOracleJDBC and data_factory), and then establishes a database interface / connection. The database interface is provided by an instance of SimpleOracleJDBC::Interface - check out the documentation of SimpleOracleJDBC to understand how to use it.
|
31
|
+
|
32
|
+
This single database interface is shared across all the test classes in the test suite. It is actually stored in a class instance variable called @@db_interface within the Test::Unit::TestCase class.
|
33
|
+
|
34
|
+
## Creating a Test
|
35
|
+
|
36
|
+
The test framework is all provided by Test::Unit, so tests can be coded in exactly the same way as usual - have a look at the Test::Unit documentation to learn about all the features of Test::Unit. An example of a very simple test is as follows:
|
37
|
+
|
38
|
+
require './test_helper'
|
39
|
+
|
40
|
+
class FirstTest < Test::Unit::TestCase
|
41
|
+
|
42
|
+
def setup
|
43
|
+
end
|
44
|
+
|
45
|
+
def teardown
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_db_connection_works
|
49
|
+
query = @@db_interface.execute_sql("select 1 from dual")
|
50
|
+
results = query.all_array
|
51
|
+
|
52
|
+
assert_equal(1, results.length, 'There should be one row in the result set')
|
53
|
+
assert_equal(1, results[0][0], 'The first and only row in the result set should contain the value 1')
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
There are two important points:
|
59
|
+
|
60
|
+
* Notice that the test_helper file is required as the first line of code. This is important as it connects to the database and loads the other required modules.
|
61
|
+
* The variable @@db_interface is available for database interactions
|
62
|
+
|
63
|
+
## Running a Test
|
64
|
+
|
65
|
+
As with simple Test::Unit test suites, tests can be executed by simply passing the test filename to JRuby:
|
66
|
+
|
67
|
+
$ jruby first_test_test.rb
|
68
|
+
Run options:
|
69
|
+
|
70
|
+
# Running tests:
|
71
|
+
|
72
|
+
.
|
73
|
+
|
74
|
+
Finished tests in 0.125000s, 8.0000 tests/s, 16.0000 assertions/s.
|
75
|
+
|
76
|
+
1 tests, 2 assertions, 0 failures, 0 errors, 0 skips
|
77
|
+
|
78
|
+
## Test Runner
|
79
|
+
|
80
|
+
Ideally, there should be one Ruby test class for each PLSQL procedure, function or package under test. In a large application there could be many files, so there needs to be a way to run all the tests in the suite. Create a file called test_runner.rb in the same directory as the tests, and put the following code in it:
|
81
|
+
|
82
|
+
current_dir = File.expand_path(File.dirname(__FILE__))
|
83
|
+
|
84
|
+
# Find all files that end in _test.rb and require them ...
|
85
|
+
files = Dir.entries(current_dir).grep(/^[^#].+_test\.rb$/).sort
|
86
|
+
files.each do |f|
|
87
|
+
require File.join(current_dir, f)
|
88
|
+
end
|
89
|
+
|
90
|
+
Now all the test files in the directory can be executed with a single command:
|
91
|
+
|
92
|
+
$ jruby test_runner.rb
|
93
|
+
|
94
|
+
For this code to work, all files that contain tests should have filenames that end in _test.rb.
|
95
|
+
|
96
|
+
# Additional Methods in Test::Unit::TestCase
|
97
|
+
|
98
|
+
PLSQL\_Unit\_Test adds a few extra methods to Test::Unit.
|
99
|
+
|
100
|
+
## Assert Methods
|
101
|
+
|
102
|
+
When testing database code, it can be useful to ensure a given table has zero, one or a number of rows for a given where clause.
|
103
|
+
|
104
|
+
To assert the users table has a single row for the username foouser:
|
105
|
+
|
106
|
+
assert_table_has_one_row("users",
|
107
|
+
"where username = 'foouser',
|
108
|
+
"Ensure the users table has a single row")
|
109
|
+
|
110
|
+
Similarly, to ensure the users table does not have a row:
|
111
|
+
|
112
|
+
assert_table_has_zero_rows("users",
|
113
|
+
"where username = 'foouser',
|
114
|
+
"Ensure the users table zero rows")
|
115
|
+
|
116
|
+
Finally, to check a table has a given number of rows:
|
117
|
+
|
118
|
+
assert_table_has_zero_rows("users",
|
119
|
+
10
|
120
|
+
"where status = 'locked'
|
121
|
+
"Ensure 10 users are locked")
|
122
|
+
|
123
|
+
## Set The Database Interface
|
124
|
+
|
125
|
+
An additional class method is available on Test::Unit::TestCase called set_db_interface. This stores the passed in object in @@db_interface. As this is a class variable, it is shared by all classes that sub-class Test::Unit::TestCase.
|
126
|
+
|
127
|
+
Test::Unit::TestCase.set_db_interface(my_db_interface)
|
128
|
+
|
129
|
+
## Oracle Dates and Times
|
130
|
+
|
131
|
+
Two further helper methods are available to format a Ruby Time object as an Oracle to_date string:
|
132
|
+
|
133
|
+
# Return a string representing the passed in Time object
|
134
|
+
# as an Oracle to_date string, down to the second
|
135
|
+
def time_as_oracle_dtm(time)
|
136
|
+
"to_date('#{time.strftime('%Y%m%d %H:%M:%S')}', 'YYYYMMDD HH24:MI:SS')"
|
137
|
+
end
|
138
|
+
|
139
|
+
# Return a string representing the passed in Time object
|
140
|
+
# as an Oracle to_date string, down to the day
|
141
|
+
def time_as_oracle_dt(time)
|
142
|
+
"to_date('#{time.strftime('%Y%m%d')}', 'YYYYMMDD')"
|
143
|
+
end
|
144
|
+
|
145
|
+
# The Setup Class
|
146
|
+
|
147
|
+
The only other class in PLSQL\_Unit\_Test is the setup class, and it contains a single class method:
|
148
|
+
|
149
|
+
def self.connect(user, password, service, host, port)
|
150
|
+
# ...
|
151
|
+
end
|
152
|
+
|
153
|
+
It takes the given parameters, creates an instance of SimpleOracleJDBC and passes it to both Test::Unit::TestCase and DataFactory::Base via their set_database_interface methods.
|
data/Rakefile.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
desc "Generate RDoc"
|
2
|
+
task :doc => ['doc:generate']
|
3
|
+
|
4
|
+
namespace :doc do
|
5
|
+
project_root = File.expand_path(File.dirname(__FILE__))
|
6
|
+
doc_destination = File.join(project_root, 'doc')
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'yard'
|
10
|
+
require 'yard/rake/yardoc_task'
|
11
|
+
|
12
|
+
YARD::Rake::YardocTask.new(:generate) do |yt|
|
13
|
+
yt.files = Dir.glob(File.join(project_root, 'lib/**/*.rb')) + #, '**', '*.rb')) +
|
14
|
+
[ File.join(project_root, 'README.md') ]
|
15
|
+
puts yt.files
|
16
|
+
yt.options = ['--output-dir', doc_destination, '--readme', 'README.md']
|
17
|
+
end
|
18
|
+
rescue LoadError
|
19
|
+
desc "Generate YARD Documentation"
|
20
|
+
task :generate do
|
21
|
+
abort "Please install the YARD gem to generate rdoc."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "Remove generated documenation"
|
26
|
+
task :clean do
|
27
|
+
rm_r doc_dir if File.exists?(doc_destination)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -1,5 +1,13 @@
|
|
1
1
|
module PLSQLUnitTest
|
2
2
|
class Setup
|
3
|
+
|
4
|
+
# Establishes a database connection using the SimpleOracleJDBC::Interface
|
5
|
+
# class. This interface is then passed to both Test::Unit::TestCase
|
6
|
+
# and DataFactory::Base using their set_database_interface methods.
|
7
|
+
#
|
8
|
+
# After calling this method, the class instance variable @@db_interface
|
9
|
+
# will be available in all sub-classes of Test::Unit::TestCase and can
|
10
|
+
# be used for database interactions.
|
3
11
|
def self.connect(user, password, service, host, port)
|
4
12
|
interface = SimpleOracleJDBC::Interface.create(user,
|
5
13
|
password,
|
@@ -1,18 +1,53 @@
|
|
1
1
|
class Test::Unit::TestCase
|
2
2
|
|
3
|
-
|
3
|
+
# Sets the class instance variable @@db_interface to the
|
4
|
+
# object that is passed in
|
4
5
|
def self.set_database_interface(interface)
|
5
6
|
@@db_interface = interface
|
6
7
|
end
|
7
8
|
|
9
|
+
# Used to assert / test a given table has only a single row.
|
10
|
+
# If a where clause is passed, it should start with 'where'. The table
|
11
|
+
# name and where clause are used to form a select statement in the form:
|
12
|
+
#
|
13
|
+
# "select count(*) from #{table} #{where_clause}"
|
14
|
+
#
|
15
|
+
# @example Assert the users table has a single row for user foouser
|
16
|
+
# assert_table_has_one_row('users',
|
17
|
+
# "where username = 'foouser'",
|
18
|
+
# "Ensure the users table has a single row")
|
19
|
+
#
|
8
20
|
def assert_table_has_one_row(table, where_clause=nil, message=nil)
|
9
21
|
assert_table_has_many_rows(table, 1, where_clause, message)
|
10
22
|
end
|
11
23
|
|
24
|
+
# Used to assert / test a given table has zero rows.
|
25
|
+
# If a where clause is passed, it should start with 'where'. The table
|
26
|
+
# name and where clause are used to form a select statement in the form:
|
27
|
+
#
|
28
|
+
# "select count(*) from #{table} #{where_clause}"
|
29
|
+
#
|
30
|
+
# @example Assert the users table has zero rows for user foouser
|
31
|
+
# assert_table_has_zero_rows('users',
|
32
|
+
# "where username = 'foouser'",
|
33
|
+
# "Ensure the users table has zero rows")
|
34
|
+
#
|
12
35
|
def assert_table_has_zero_rows(table, where_clause=nil, message=nil)
|
13
36
|
assert_table_has_many_rows(table, 0, where_clause, message)
|
14
37
|
end
|
15
38
|
|
39
|
+
# Used to assert / test a given table has a given number of rows.
|
40
|
+
# If a where clause is passed, it should start with 'where'. The table
|
41
|
+
# name and where clause are used to form a select statement in the form:
|
42
|
+
#
|
43
|
+
# "select count(*) from #{table} #{where_clause}"
|
44
|
+
#
|
45
|
+
# @example Assert the users table has zero rows for user foouser
|
46
|
+
# assert_table_has_many_rows('users',
|
47
|
+
# 10
|
48
|
+
# "where status = 'locked'",
|
49
|
+
# "Ensure the users table has 10 locked rows")
|
50
|
+
#
|
16
51
|
def assert_table_has_many_rows(table, row_count, where_clause=nil, message=nil)
|
17
52
|
results = @@db_interface.execute_sql("select count(*) from #{table} #{where_clause}").all_array
|
18
53
|
message = build_message(message, "A count of <?> was expected but was <?> for <?> <?>", row_count.to_s, results[0][0].to_i, table, where_clause)
|
@@ -21,10 +56,14 @@ class Test::Unit::TestCase
|
|
21
56
|
end
|
22
57
|
end
|
23
58
|
|
59
|
+
# Returns a string representing an Ruby Time object in Oracle to_date
|
60
|
+
# format, accurate to a second.
|
24
61
|
def time_as_oracle_dtm(time)
|
25
62
|
"to_date('#{time.strftime('%Y%m%d %H:%M:%S')}', 'YYYYMMDD HH24:MI:SS')"
|
26
63
|
end
|
27
64
|
|
65
|
+
# Returns a string representing an Ruby Time object in Oracle to_date
|
66
|
+
# format, accurate to a day.
|
28
67
|
def time_as_oracle_dt(time)
|
29
68
|
"to_date('#{time.strftime('%Y%m%d')}', 'YYYYMMDD')"
|
30
69
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plsql_unit_test
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,14 +9,14 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: data_factory
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
|
-
- - '
|
19
|
+
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: 0.1.2
|
22
22
|
type: :runtime
|
@@ -24,7 +24,7 @@ dependencies:
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
26
|
requirements:
|
27
|
-
- - '
|
27
|
+
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 0.1.2
|
30
30
|
- !ruby/object:Gem::Dependency
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
none: false
|
34
34
|
requirements:
|
35
|
-
- - '
|
35
|
+
- - ! '>='
|
36
36
|
- !ruby/object:Gem::Version
|
37
37
|
version: 0.1.1
|
38
38
|
type: :runtime
|
@@ -40,7 +40,7 @@ dependencies:
|
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
42
42
|
requirements:
|
43
|
-
- - '
|
43
|
+
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: 0.1.1
|
46
46
|
description: Monkey patches Test::Unit::TestCase to add some additional assert methods,
|
@@ -55,6 +55,7 @@ files:
|
|
55
55
|
- lib/plsql_unit_test/setup.rb
|
56
56
|
- lib/plsql_unit_test/test_unit_patch.rb
|
57
57
|
- lib/plsql_unit_test.rb
|
58
|
+
- Rakefile.rb
|
58
59
|
- README.md
|
59
60
|
homepage: http://betteratoracle.com
|
60
61
|
licenses: []
|