plsql_unit_test 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|