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

Skip to content

Commit d12bec1

Browse files
committed
Addressed review comments.
Added documentations. There are 3 options now for parameter `a_exclude` filtering columns/attributes: 1. A `varchar2` containing comma separated names of columns/attributes to exclude 2. A `varchar2` containing **XPath** expression that lists items to be excluded 3. A `ut_varchar2_list` containing list of columns/attributes to exclude
1 parent 1abadfe commit d12bec1

11 files changed

Lines changed: 150 additions & 46 deletions

docs/userguide/expectations.md

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -211,24 +211,55 @@ end;
211211
```
212212
The `a_nulls_are_equal` parameter controls the behavior of a `null=null` comparison (**this comparison by default is true!**)
213213

214-
### Comparing cursors
214+
### Excluding columns and attributes from comparison
215215

216-
The `equal` matcher accepts an additional parameter `a_exclude varchar2` or `a_exclude ut_varchar2_list`, when used to compare `cursor` data.
216+
The `equal` matcher accepts an additional parameter `a_exclude` when used to compare data of `cursor`, `object` or `table type`.
217217

218-
These parameters enable a list of column names to be passed for exclusion from the data comparison. The list can be a comma separated `varchar2` list or a `ut_varchar2_list` collection.
219-
The column names accepted by the parameter are **case sensitive** and cannot be quoted.
220-
If the `a_exclude` parameter is not specified, all columns are included.
221-
If a column to be excluded does not exist, the column cannot be excluded and it's name is simply ignored.
222-
This is useful when testing cursors containing data that is beyond our control (like default or trigger/procedure generated sysdate values on columns).
218+
This parameter can take three forms:
219+
1. A `varchar2` containing comma separated names of columns/attributes to exclude
220+
2. A `varchar2` containing **XPath** expression that lists items to be excluded
221+
3. A `ut_varchar2_list` containing list of columns/attributes to exclude
222+
223+
The column/attribute names are **case sensitive** and cannot be quoted.
224+
If the `a_exclude` parameter is not specified, whole cursor/object/table type is compared.
225+
If a column/attribute to be excluded does not exist it is simply ignored (no error).
226+
227+
This is useful when testing elements data that cannot be determined or set up by the tests (like `sysdate` populated by default on audit columns).
223228

224229
```sql
225230
procedure test_cursors_skip_columns is
226-
x sys_refcursor;
227-
y sys_refcursor;
231+
l_expected sys_refcursor;
232+
l_actual sys_refcursor;
228233
begin
229-
open x for select 'text' ignore_me, d.* from user_tables d;
230-
open y for select sysdate "ADate", d.* from user_tables d;
231-
ut.expect( a_actual => y ).to_equal( a_expected => x, a_exclude => 'IGNORE_ME,ADate' );
234+
open l_expected for select 'text' ignore_me, d.* from user_tables d;
235+
open l_actual for select sysdate "ADate", d.* from user_tables d;
236+
ut.expect( l_actual ).to_equal( l_expected, a_exclude => 'IGNORE_ME,ADate' );
237+
end;
238+
```
239+
240+
```sql
241+
create or replace type employee as object(
242+
first_name varchar2(50),
243+
last_name varchar2(50),
244+
hire_date date,
245+
created_at timestamp,
246+
created_by varchar2(30),
247+
modified_at timestamp,
248+
modified_by varchar2(50)
249+
);
250+
251+
procedure test_object_skip_columns is
252+
l_expected employee;
253+
l_actual employee;
254+
begin
255+
l_expected := employee('John'||rownum, 'Doe', sysdate, systimestamp, 'me', systimestamp, 'me');
256+
-- the actual should normally be returned by the tested code.
257+
l_actual := employee('John'||rownum, 'Doe', sysdate, systimestamp, 'me', systimestamp, 'me');
258+
259+
-- test the data excluding attributes specified by XPath
260+
ut.expect( anydata.convertObject(l_actual) ).to_equal(
261+
anydata.convertObject(l_expected), a_exclude => '/EMPLOYEE/CREATED_AT|/EMPLOYEE/MODIFIED_AT'
262+
);
232263
end;
233264
```
234265

source/core/ut_utils.pkb

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -328,27 +328,25 @@ create or replace package body ut_utils is
328328
end if;
329329
end;
330330

331-
function to_xpath(a_list varchar2) return varchar2 is
331+
function to_xpath(a_list varchar2, a_ancestors varchar2 := '/*/') return varchar2 is
332332
l_xpath varchar2(32767) := a_list;
333333
begin
334334
if l_xpath not like '/%' then
335-
l_xpath := '//'||replace(l_xpath,',','|//');
335+
l_xpath := to_xpath( clob_to_table(a_clob=>a_list, a_delimiter=>','), a_ancestors);
336336
end if;
337337
return l_xpath;
338338
end;
339339

340-
function to_xpath(a_list ut_varchar2_list) return varchar2 is
340+
function to_xpath(a_list ut_varchar2_list, a_ancestors varchar2 := '/*/') return varchar2 is
341341
l_xpath varchar2(32767);
342+
l_item varchar2(32767);
342343
i integer;
343344
begin
344345
i := a_list.first;
345346
while i is not null loop
346-
if a_list(i) is not null then
347-
if a_list(i) not like '/%' then
348-
l_xpath := l_xpath || '//'||a_list(i)||'|';
349-
else
350-
l_xpath := l_xpath ||a_list(i)||'|';
351-
end if;
347+
l_item := trim(a_list(i));
348+
if l_item is not null then
349+
l_xpath := l_xpath || a_ancestors ||a_list(i)||'|';
352350
end if;
353351
i := a_list.next(i);
354352
end loop;

source/core/ut_utils.pks

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,9 @@ create or replace package ut_utils authid definer is
212212
procedure append_to_clob(a_src_clob in out nocopy clob, a_new_data clob);
213213
procedure append_to_clob(a_src_clob in out nocopy clob, a_new_data varchar2);
214214

215-
function to_xpath(a_list varchar2) return varchar2;
215+
function to_xpath(a_list varchar2, a_ancestors varchar2 := '/*/') return varchar2;
216216

217-
function to_xpath(a_list ut_varchar2_list) return varchar2;
217+
function to_xpath(a_list ut_varchar2_list, a_ancestors varchar2 := '/*/') return varchar2;
218218

219219
end ut_utils;
220220
/

source/expectations/data_values/ut_data_value_anydata.tpb

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ create or replace type body ut_data_value_anydata as
8181
self.data_type := case when a_value is not null then lower(a_value.gettypename) else 'undefined' end;
8282
end;
8383

84-
static function get_instance(a_data_value anydata) return ut_data_value_anydata is
84+
static function get_instance(a_data_value anydata, a_exclude varchar2 := null) return ut_data_value_anydata is
8585
l_result ut_data_value_anydata := ut_data_value_object(null);
8686
l_type anytype;
8787
l_type_code integer;
@@ -90,28 +90,23 @@ create or replace type body ut_data_value_anydata as
9090
l_type_code := a_data_value.gettype(l_type);
9191
if l_type_code = dbms_types.typecode_object then
9292
l_result := ut_data_value_object(a_data_value);
93+
l_result.exclude_xpath := ut_utils.to_xpath(a_exclude);
9394
elsif l_type_code in (dbms_types.typecode_table, dbms_types.typecode_varray, dbms_types.typecode_namedcollection) then
9495
l_result := ut_data_value_collection(a_data_value);
96+
l_result.exclude_xpath := ut_utils.to_xpath(a_exclude,'/*/*/');
9597
else
9698
raise_application_error(-20000, 'Data type '||a_data_value.gettypename||' in ANYDATA is not supported by utPLSQL');
9799
end if;
98100
end if;
99101
return l_result;
100102
end;
101103

102-
static function get_instance(a_data_value anydata, a_exclude varchar2) return ut_data_value_anydata is
103-
l_result ut_data_value_anydata;
104-
begin
105-
l_result := ut_data_value_anydata.get_instance(a_data_value);
106-
l_result.exclude_xpath := ut_utils.to_xpath(a_exclude);
107-
return l_result;
108-
end;
109-
110104
static function get_instance(a_data_value anydata, a_exclude ut_varchar2_list) return ut_data_value_anydata is
111105
l_result ut_data_value_anydata;
106+
l_exclude varchar2(32767);
112107
begin
113-
l_result := ut_data_value_anydata.get_instance(a_data_value);
114-
l_result.exclude_xpath := ut_utils.to_xpath(a_exclude);
108+
l_exclude := substr(ut_utils.table_to_clob(a_exclude, ','), 1, 32767);
109+
l_result := ut_data_value_anydata.get_instance(a_data_value, l_exclude );
115110
return l_result;
116111
end;
117112

source/expectations/data_values/ut_data_value_anydata.tps

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,14 @@ create or replace type ut_data_value_anydata under ut_data_value(
1818
data_value anydata,
1919
/*
2020
var: exclude_xpath
21-
Holds xpath (list of columns) to exclude when comparing cursor
21+
Holds xpath (list of columns) to exclude when comparing object
2222
*/
2323
exclude_xpath varchar2(32767),
2424
overriding member function is_null return boolean,
2525
overriding member function to_string return varchar2,
2626
overriding member function compare_implementation( a_other ut_data_value ) return integer,
2727
final member procedure init(self in out nocopy ut_data_value_anydata, a_value anydata, a_self_type varchar2),
28-
static function get_instance(a_data_value anydata) return ut_data_value_anydata,
29-
static function get_instance(a_data_value anydata, a_exclude varchar2) return ut_data_value_anydata,
28+
static function get_instance(a_data_value anydata, a_exclude varchar2 := null) return ut_data_value_anydata,
3029
static function get_instance(a_data_value anydata, a_exclude ut_varchar2_list) return ut_data_value_anydata
3130
) not final not instantiable
3231
/

tests/RunAll.sql

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ exec ut_coverage.coverage_start_develop();
6767
@@lib/RunTest.sql ut_expectations/ut.expect.to_be_true.GivesFailureWhenExpessionIsNotBoolean.sql
6868
@@lib/RunTest.sql ut_expectations/ut.expect.to_be_true.GivesFailureWhenExpessionIsNull.sql
6969
@@lib/RunTest.sql ut_expectations/ut.expect.to_be_true.GivesSuccessWhenExpessionIsTrue.sql
70+
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.anydata.ExcludeColumnsXPathFailsOnInvalidXPath.sql
7071
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.anydata.GivesFailureWhenBothObjectsAreNullButDifferentType.sql
7172
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.anydata.GivesFailureWhenComparingDifferentData.sql
7273
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.anydata.GivesFailureWhenComparingDifferentObjects.sql
@@ -78,12 +79,14 @@ exec ut_coverage.coverage_start_develop();
7879
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.anydata.GivesSuccessWhenComparingTheSameData.sql
7980
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.anydata.PutsObjectStrucureIntoResult.sql
8081
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.anydata.GivesSuccessWithExcludedCollectionAttributes.sql
82+
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.anydata.GivesSuccessWithExcludedCollectionAttributesXPath.sql
8183
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.anydata.GivesSuccessWithExcludedObjectAttributes.sql
84+
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.anydata.GivesSuccessWithExcludedObjectAttributesXPath.sql
8285
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.cursor.ComparesDateAndTimeWhenSetNlsIsUsed.sql
8386
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.cursor.ExcludeColumnNameListIsCaseSensitive.sql
84-
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.cursor.ExcludeColumnNameListXpathIsCaseSensitive.sql
8587
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.cursor.ExcludeColumnNamesAreCaseSensitive.sql
86-
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.cursor.ExcludeColumnNamesXPathAreCaseSensitive.sql
88+
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.cursor.ExcludeColumnsXPathFailsOnInvalidXPath.sql
89+
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.cursor.ExcludeColumnsXPathIsCaseSensitive.sql
8790
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.cursor.GivesFailureForDifferentValues.sql
8891
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.cursor.GivesSuccessForEqualValues.sql
8992
@@lib/RunTest.sql ut_expectations/ut.expect.to_equal.cursor.GivesSuccessForEqualValuesWithExcludedColumnList.sql
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--Arrange
2+
declare
3+
l_actual ut_key_value_pair;
4+
l_expected ut_key_value_pair;
5+
l_result integer;
6+
e_xpath_error exception;
7+
pragma exception_init (e_xpath_error,-31011);
8+
begin
9+
--Act
10+
l_actual := ut_key_value_pair(key=>'A',value=>'1');
11+
l_expected := ut_key_value_pair(key=>'A',value=>'0');
12+
begin
13+
ut.expect(anydata.convertObject(l_actual)).to_equal(anydata.convertObject(l_expected), a_exclude=>'/ROW/A_COLUMN,//Some_Col');
14+
exception
15+
when e_xpath_error then
16+
l_result := ut_utils.tr_success;
17+
end;
18+
--Assert
19+
if nvl(:test_result, ut_utils.tr_success) = ut_utils.tr_success and l_result = ut_utils.tr_success then
20+
:test_result := ut_utils.tr_success;
21+
else
22+
dbms_output.put_line( 'Expected exception ORA-31011: XML parsing failed, but nothing was raised');
23+
end if;
24+
end;
25+
/
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--Arrange
2+
declare
3+
l_actual ut_key_value_pairs;
4+
l_expected ut_key_value_pairs;
5+
l_result integer;
6+
l_results_details ut_expectation_results;
7+
begin
8+
--Act
9+
l_actual := ut_key_value_pairs(ut_key_value_pair(key=>'A',value=>'1'), ut_key_value_pair(key=>'B',value=>'2'));
10+
l_expected := ut_key_value_pairs(ut_key_value_pair(key=>'A',value=>'0'), ut_key_value_pair(key=>'B',value=>'1'));
11+
12+
ut.expect(anydata.convertCollection(l_actual)).to_equal(
13+
anydata.convertCollection(l_expected),
14+
a_exclude=>ut_varchar2_list('/UT_KEY_VALUE_PAIRS/UT_KEY_VALUE_PAIR/VALUE')
15+
);
16+
l_result := ut_expectation_processor.get_status();
17+
l_results_details := ut_expectation_processor.get_expectations_results();
18+
--Assert
19+
if nvl(:test_result, ut_utils.tr_success) = ut_utils.tr_success and l_result = ut_utils.tr_success then
20+
:test_result := ut_utils.tr_success;
21+
else
22+
for i in 1 .. l_results_details.count loop
23+
if l_results_details(i).status != ut_utils.tr_success then
24+
dbms_output.put_line( l_results_details(i).message );
25+
end if;
26+
end loop;
27+
end if;
28+
end;
29+
/

tests/ut_expectations/ut.expect.to_equal.cursor.ExcludeColumnNameListXpathIsCaseSensitive.sql renamed to tests/ut_expectations/ut.expect.to_equal.anydata.GivesSuccessWithExcludedObjectAttributesXPath.sql

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
PROMPT Exclude column name list are case sensitive
21
--Arrange
32
declare
4-
l_actual SYS_REFCURSOR;
5-
l_expected SYS_REFCURSOR;
3+
l_actual ut_key_value_pair;
4+
l_expected ut_key_value_pair;
65
l_result integer;
76
l_results_details ut_expectation_results;
87
begin
98
--Act
10-
open l_actual for select 'a' as "A_Column", 'c' as A_COLUMN, 'x' SOME_COL, 'd' "Some_Col" from dual;
11-
open l_expected for select 'a' as "A_Column", 'd' as A_COLUMN, 'x' SOME_COL, 'c' "Some_Col" from dual;
12-
ut.expect(l_actual).to_equal(l_expected, a_exclude=>ut_varchar2_list('/ROW/A_COLUMN','//Some_Col'));
9+
l_actual := ut_key_value_pair(key=>'A',value=>'1');
10+
l_expected := ut_key_value_pair(key=>'A',value=>'0');
11+
12+
ut.expect(anydata.convertObject(l_actual)).to_equal(anydata.convertObject(l_expected), a_exclude=>'/UT_KEY_VALUE_PAIR/VALUE');
1313
l_result := ut_expectation_processor.get_status();
1414
l_results_details := ut_expectation_processor.get_expectations_results();
1515
--Assert
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--Arrange
2+
declare
3+
l_actual SYS_REFCURSOR;
4+
l_expected SYS_REFCURSOR;
5+
l_result integer;
6+
e_xpath_error exception;
7+
pragma exception_init (e_xpath_error,-31011);
8+
begin
9+
--Act
10+
open l_actual for select 'a' as "A_Column", 'c' as A_COLUMN, 'x' SOME_COL, 'd' "Some_Col" from dual;
11+
open l_expected for select 'a' as "A_Column", 'd' as A_COLUMN, 'x' SOME_COL, 'c' "Some_Col" from dual;
12+
begin
13+
ut.expect(l_actual).to_equal(l_expected, a_exclude=>'/ROW/A_COLUMN,//Some_Col');
14+
exception
15+
when e_xpath_error then
16+
l_result := ut_utils.tr_success;
17+
end;
18+
--Assert
19+
if nvl(:test_result, ut_utils.tr_success) = ut_utils.tr_success and l_result = ut_utils.tr_success then
20+
:test_result := ut_utils.tr_success;
21+
else
22+
dbms_output.put_line( 'Expected exception ORA-31011: XML parsing failed, but nothing was raised');
23+
end if;
24+
end;
25+
/

0 commit comments

Comments
 (0)