Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d393dd0

Browse files
authored
Merge pull request #291 from jgebal/feature/refactor_data_values
Refactored ut_data_value, ut_expectation and ut_matchers. Merging this PR as there are no objections. It opens doors for further improvements with the restructuring that was done.
2 parents d468aa9 + 1b5cd2d commit d393dd0

131 files changed

Lines changed: 1992 additions & 969 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.travis/install.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ set feedback off
99
set verify off
1010
1111
@../source/create_utplsql_owner.sql $UT3_OWNER $UT3_OWNER_PASSWORD $UT3_TABLESPACE
12-
12+
--needed for Mystats script to work
13+
grant select any dictionary to $UT3_OWNER;
1314
@../source/create_utplsql_owner.sql $UT3_USER $UT3_USER_PASSWORD $UT3_TABLESPACE
1415
cd ..
1516

docs/userguide/expectations.md

Lines changed: 171 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Validation of the code under test (the tested logic of procedure/function etc.) is performed by comparing the actual data against the expected data.
44
To do that we use concept of expectation and a matcher to perform the check on the data.
55

6-
It's best to give an example to get an idea what is what
6+
Example of unit test procedure body with a single expectation.
77
```sql
88
begin
99
ut.expect( 'the tested value' ).to_( equal('the expected value') );
@@ -41,16 +41,18 @@ Validates that the actual value is between the lower and upper bound.
4141

4242
Example:
4343
```sql
44-
exec ut.expect( a_actual => 3 ).to_( be_between( a_lower_bound => 1, a_upper_bound => 3 ) );
45-
exec ut.expect( 3 ).to_( be_between( 1, 3 ) );
44+
begin
45+
ut.expect( a_actual => 3 ).to_( be_between( a_lower_bound => 1, a_upper_bound => 3 ) );
46+
ut.expect( 3 ).to_( be_between( 1, 3 ) );
47+
end;
4648
```
4749

4850
## be_empty
4951
Unary matcher that validates if the provided data-set is empty.
5052

5153
Usage:
5254
```sql
53-
declare
55+
procedure test_if_cursor_is_empty is
5456
l_cursor sys_refcursor;
5557
begin
5658
open l_cursor for select * from dual where 1 = 0;
@@ -65,39 +67,49 @@ Unary matcher that validates if the provided value is false.
6567

6668
Usage:
6769
```sql
68-
exec ut.expect( ( 1 = 0 ) ).to_( be_false() );
70+
begin
71+
ut.expect( ( 1 = 0 ) ).to_( be_false() );
72+
end;
6973
```
7074

7175
## be_greater_or_equal
7276
Allows to check if the actual value is greater or equal than the expected.
7377

7478
Usage:
7579
```sql
76-
exec ut.expect( sysdate ).to_( be_greater_or_equal( sysdate - 1 ) );
80+
begin
81+
ut.expect( sysdate ).to_( be_greater_or_equal( sysdate - 1 ) );
82+
end;
7783
```
7884

7985
## be_greater_than
8086
Allows to check if the actual value is greater than the expected.
8187

8288
Usage:
8389
```sql
84-
exec ut.expect( 2 ).to_( be_greater_than( 1 ) );
90+
begin
91+
ut.expect( 2 ).to_( be_greater_than( 1 ) );
92+
end;
8593
```
8694

8795
## be_less_or_equal
8896
Allows to check if the actual value is less or equal than the expected.
8997

9098
Usage:
9199
```sql
92-
exec ut.expect( 3 ).to_( be_less_or_equal( 3 ) );
100+
begin
101+
ut.expect( 3 ).to_( be_less_or_equal( 3 ) );
102+
end;
93103
```
94104

95105
## be_less_than
96106
Allows to check if the actual value is less than the expected.
97107

98108
Usage:
99109
```sql
100-
exec ut.expect( 3 ).to_( be_less_than( 2 ) );
110+
begin
111+
ut.expect( 3 ).to_( be_less_than( 2 ) );
112+
end;
101113
```
102114

103115

@@ -106,8 +118,10 @@ Validates that the actual value is like the expected expression.
106118

107119
Usage:
108120
```sql
109-
exec ut.expect( 'Lorem_impsum' ).to_( be_like( a_mask => '%rem\_%', a_escape_char => '\' ) );
110-
exec ut.expect( 'Lorem_impsum' ).to_( be_like( '%rem\_%', '\' ) );
121+
begin
122+
ut.expect( 'Lorem_impsum' ).to_( be_like( a_mask => '%rem\_%', a_escape_char => '\' ) );
123+
ut.expect( 'Lorem_impsum' ).to_( be_like( '%rem\_%', '\' ) );
124+
end;
111125
```
112126
113127
Parameters `a_mask` and `a_escape_char` represent a valid parameters of the [Oracle like function](https://docs.oracle.com/database/121/SQLRF/conditions007.htm#SQLRF52142)
@@ -118,15 +132,19 @@ Unary matcher that validates if the actual value is not null.
118132
119133
Usage:
120134
```sql
121-
exec ut.expect( to_clob('ABC') ).to_( be_not_null() );
135+
begin
136+
ut.expect( to_clob('ABC') ).to_( be_not_null() );
137+
end;
122138
```
123139
124140
## be_null
125141
Unary matcher that validates if the actual value is null.
126142
127143
Usage:
128144
```sql
129-
exec ut.expect( cast(null as varchar2(100)) ).to_( be_null() );
145+
begin
146+
ut.expect( cast(null as varchar2(100)) ).to_( be_null() );
147+
end;
130148
```
131149
132150
## be_true
@@ -135,39 +153,152 @@ Unary matcher that validates if the provided value is false.
135153
136154
Usage:
137155
```sql
138-
exec ut.expect( ( 1 = 1 ) ).to_( be_true() );
156+
begin
157+
ut.expect( ( 1 = 1 ) ).to_( be_true() );
158+
end;
139159
```
140160
141161
## equal
142162
143-
The equal matcher is a very restrictive matcher. It only returns true, if compared data-types.
163+
The equal matcher is a very restrictive matcher. It only returns true, if compared data-types are the same.
144164
That means, that comparing varchar2 to a number will fail even if the varchar2 contains the same number.
145165
This matcher is designed to capture changes of data-type, so that if you expect your variable to be number and is now something else,
146166
the test will fail and give you early indication of potential problem.
147167
148168
Usage:
149169
```sql
150-
declare
151-
x ref_cursor;
152-
y ref_cursor;
170+
procedure check_if_cursors_are_equal is
171+
x sys_refcursor;
172+
y sys_refcursor;
153173
begin
154174
ut.expect( 'a dog' ).to_( equal( 'a dog' ) );
155175
ut.expect( a_actual => y ).to_( equal( a_expected => x, a_nulls_are_equal => true ) );
156176
end;
157177
```
158178
The `a_nulls_are_equal` parameter decides on the behavior of `null=null` comparison (**this comparison by default is true!**)
159179
180+
### Comparing cursors
181+
182+
The `equal` matcher accepts additional parameter `a_exclude varchar2` or `a_exclude ut_varchar2_list`, when used to compare `cursor` data.
183+
Those parameters allow passing a list of column names to exclude from data comparison. The list can be a comma separated `varchar2` list or a `ut_varchar2_list` collection.
184+
The column names accepted by parameter are **case sensitive** and cannot be quoted.
185+
If `a_exclude` parameter is not specified, all columns are included.
186+
If a column to be excluded does not exist, the column cannot be excluded and it's name is simply ignored.
187+
It is useful when testing cursors containing data that is beyond our control (like default or trigger/procedure generated sysdate values on columns).
188+
189+
```sql
190+
procedure test_cursors_skip_columns is
191+
x sys_refcursor;
192+
y sys_refcursor;
193+
begin
194+
open x for select 'text' ignore_me, d.* from user_tables d;
195+
open y for select sysdate "ADate", d.* from user_tables d;
196+
ut.expect( a_actual => y ).to_( equal( a_expected => x, a_exclude => 'IGNORE_ME,ADate' ) );
197+
end;
198+
```
199+
200+
### Comparing cursor data containing DATE fields
201+
202+
**Important note**
203+
utPLSQL uses XMLType internally to represent rows of the cursor data. This is by far most flexible and allows comparison of cursors containing LONG, CLOB, BLOB, user defined types and even nested cursors.
204+
Due to the way Oracle handles DATE data type when converting from cursor data to XML, utPLSQL has no control over the DATE formatting.
205+
The NLS_DATE_FORMAT setting from the moment the cursor was opened decides ont the formatting of dates used for cursor data comparison.
206+
By default, Oracle NLS_DATE_FORMAT is timeless, so data of DATE datatype, will be compared ignoring the time part of it.
207+
208+
You should use procedures `ut.set_nls`, `ut.reset_nls` around cursors that you want to compare in your tests.
209+
This way, the DATE data in cursors will get properly formatted for comparison using date-time format.
210+
211+
The example below makes use of `ut.set_nls`, `ut.reset_nls`, so that date in `l_expected` and `l_actual` is compared using date-time formatting.
212+
```sql
213+
create table events (
214+
description varchar2(4000),
215+
event_date date
216+
);
217+
218+
create or replace function get_events(a_date_from date, a_date_to date) return sys_refcursor is
219+
l_result sys_refcursor;
220+
begin
221+
open l_result for
222+
select description, event_date
223+
from events
224+
where event_date between a_date_from and a_date_to;
225+
return l_result;
226+
end;
227+
/
228+
229+
create or replace package test_get_events is
230+
231+
--%suite(get_events)
232+
233+
--%beforeall
234+
procedure setup_events;
235+
236+
--%test(returns event within date range)
237+
procedure get_events_for_date_range;
238+
239+
end;
240+
/
241+
242+
create or replace package body test_get_events is
243+
244+
gc_description constant varchar2(30) := 'Test event';
245+
gc_event_date constant date := to_date('2016-09-08 06:51:22','yyyy-mm-dd hh24:mi:ss');
246+
procedure setup_events is
247+
begin
248+
insert into events (description, event_date)
249+
values (gc_description, gc_event_date);
250+
end;
251+
252+
procedure get_events_for_date_range is
253+
l_expected sys_refcursor;
254+
l_actual sys_refcursor;
255+
l_expected_bad_date sys_refcursor;
256+
l_second number := 1/24/60/60;
257+
begin
258+
ut.set_nls();
259+
open l_expected for select gc_description as description, gc_event_date as event_date from dual;
260+
open l_expected_bad_date for select gc_description as description, gc_event_date + l_second as event_date from dual;
261+
l_actual := get_events(gc_event_date-1, gc_event_date+1);
262+
ut.reset_nls();
263+
264+
ut.expect(l_actual).to_( equal(l_expected) );
265+
ut.expect(l_actual).not_to( equal(l_expected_bad_date) );
266+
end;
267+
268+
end;
269+
/
270+
271+
begin
272+
ut.run();
273+
end;
274+
/
275+
276+
drop table events;
277+
drop function get_events;
278+
drop package test_get_events;
279+
```
280+
281+
### Comparing user defined types and collections
282+
160283
The `anydata` data type is used to compare user defined object and collections.
161284

162285
Example usage of anydata to compare user defined types.
163286
```sql
164287
create type department as object(name varchar2(30));
165288
/
289+
290+
create type departments as table of department;
291+
/
292+
166293
create or replace package demo_dept as
167294
-- %suite(demo)
168295
169-
--%test(demo_dept)
296+
--%test(demo of object to object comparison)
170297
procedure test_department;
298+
299+
--%test(demo of collection comparison)
300+
procedure test_departments;
301+
171302
end;
172303
/
173304
@@ -177,8 +308,19 @@ create or replace package body demo_dept as
177308
v_actual department;
178309
begin
179310
v_expected := department('HR');
311+
v_actual := department('IT');
180312
ut.expect( anydata.convertObject(v_expected) ).to_( equal( anydata.convertObject(v_actual) ) );
181313
end;
314+
315+
procedure test_department is
316+
v_expected department;
317+
v_actual department;
318+
begin
319+
v_expected := departments(department('HR'));
320+
v_actual := departments(department('IT'));
321+
ut.expect( anydata.convertCollection(v_expected) ).to_( equal( anydata.convertCollection(v_actual) ) );
322+
end;
323+
182324
end;
183325
/
184326
```
@@ -190,8 +332,10 @@ Validates that the actual value is matching the expected regular expression.
190332

191333
Usage:
192334
```sql
193-
exec ut.expect( a_actual => '123-456-ABcd' ).to_( match( a_pattern => '\d{3}-\d{3}-[a-z]', a_modifiers => 'i' ) );
194-
exec ut.expect( 'some value' ).to_( match( '^some.*' ) );
335+
begin
336+
ut.expect( a_actual => '123-456-ABcd' ).to_( match( a_pattern => '\d{3}-\d{3}-[a-z]', a_modifiers => 'i' ) );
337+
ut.expect( 'some value' ).to_( match( '^some.*' ) );
338+
end;
195339
```
196340

197341
Parameters `a_pattern` and `a_modifiers` represent a valid regexp pattern accepted by [Oracle regexp_like function](https://docs.oracle.com/database/121/SQLRF/conditions007.htm#SQLRF00501)
@@ -225,12 +369,16 @@ Expectations provide a very convenient way to check for a negative of the expect
225369

226370
Syntax of check for matcher evaluating to true:
227371
```sql
228-
exec ut.expect( a_actual {data-type} ).to_( {matcher} );
372+
begin
373+
ut.expect( a_actual {data-type} ).to_( {matcher} );
374+
end;
229375
```
230376

231377
Syntax of check for matcher evaluating to false:
232378
```sql
233-
exec ut.expect( a_actual {data-type} ).not_to( {matcher} );
379+
begin
380+
ut.expect( a_actual {data-type} ).not_to( {matcher} );
381+
end;
234382
```
235383

236384
If a matcher evaluated to NULL, then both `to_` and `not_to` will cause the expectation to report failure.

source/api/ut.pkb

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@ create or replace package body ut is
1717
limitations under the License.
1818
*/
1919

20+
g_nls_date_format varchar2(4000);
21+
2022
function version return varchar2 is
2123
begin
2224
return ut_utils.gc_version;
2325
end;
2426

2527
function expect(a_actual in anydata, a_message varchar2 := null) return ut_expectation_anydata is
2628
begin
27-
return ut_expectation_anydata(ut_data_value_anydata(a_actual), a_message);
29+
return ut_expectation_anydata(ut_data_value_anydata.get_instance(a_actual), a_message);
2830
end;
2931

3032
function expect(a_actual in blob, a_message varchar2 := null) return ut_expectation_blob is
@@ -164,5 +166,24 @@ create or replace package body ut is
164166
ut.run(l_paths, a_reporter, a_color_console);
165167
end;
166168

169+
procedure set_nls is
170+
begin
171+
if g_nls_date_format is null then
172+
select nsp.value
173+
into g_nls_date_format
174+
from nls_session_parameters nsp
175+
where parameter = 'NLS_DATE_FORMAT';
176+
end if;
177+
execute immediate 'alter session set nls_date_format = '''||ut_utils.gc_date_format||'''';
178+
end;
179+
180+
procedure reset_nls is
181+
begin
182+
if g_nls_date_format is not null then
183+
execute immediate 'alter session set nls_date_format = '''||g_nls_date_format||'''';
184+
end if;
185+
g_nls_date_format := null;
186+
end;
187+
167188
end ut;
168189
/

0 commit comments

Comments
 (0)