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

Skip to content

Commit bbdc5bd

Browse files
authored
Merge pull request #591 from utPLSQL/feature/add_cursor_column_check
Add Diff functionality to cursors, collections and object types
2 parents 031a732 + 768a197 commit bbdc5bd

30 files changed

Lines changed: 1184 additions & 366 deletions

docs/userguide/advanced_data_comparison.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ When using XPath expression, keep in mind the following:
9898
- object type attributes are nested under `<OBJECTY_TYPE>` element
9999
- nested table and varray items type attributes are nested under `<ARRAY><OBJECTY_TYPE>` elements
100100

101+
Example of a valid XPath parameter to include columns: `RN`, `A_Column`, `SOME_COL` in data comparison.
101102
```sql
102103
procedure include_columns_as_xpath is
103104
l_actual sys_refcursor;

docs/userguide/expectations.md

Lines changed: 125 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ The matcher will also fail when comparing a `timestamp` to a `timestamp with tim
338338
The matcher enables detection data-type changes.
339339
If you expect your variable to be a number and it is now some other type, the test will fail and give you early indication of a potential problem.
340340

341-
To keep it simple, the `equal` will only succeed if you compare apples to apples.
341+
To keep it simple, the `equal` matcher will only succeed if you compare apples to apples.
342342

343343
Example usage
344344
```sql
@@ -415,8 +415,9 @@ create or replace package body test_animals_getter is
415415
end;
416416
```
417417

418-
**Comparing NULLs is by default!**
418+
**Comparing NULLs is by default a success!**
419419
The `a_nulls_are_equal` parameter controls the behavior of a `null = null` comparison.
420+
To change the behavior of `NULL = NULL` comparison pass the `a_nulls_are_equal => false` to the `equal` matcher.
420421

421422

422423
## Comparing objects, cursors, collections of data
@@ -427,9 +428,9 @@ utPLSQL is capable of comparing compound data-types including:
427428
- nested table/varray types
428429

429430
### Notes on comparison of compound data
430-
- Compound data can contain elements of any data-type. This includes blob, clob, object type, nested table, varray or nested-cursor.
431+
- Compound data can contain elements of any data-type. This includes blob, clob, object type, nested table, varray or even a nested-cursor within a cursor.
431432
- Cursors, nested table and varray types are compared as **ordered lists of elements**. If order of elements differ, expectation will fail.
432-
- Comparison of compound data does not currently support data-type check on attribute/column level. This might be changed in the future.
433+
- Comparison of compound data is data-type aware. So a column `ID NUMBER` in a cursor is not the same as `ID VARCHAR2(100)`, even if they both hold the same numeric values.
433434
- Comparison of cursor columns containing `DATE` will only compare date part **and ignore time** by default. See [Comparing cursor data containing DATE fields](#comparing-cursor-data-containing-date-fields) to check how to enable date-time comparison in cursors.
434435
- To compare nested table/varray type you need to convert it to `anydata` by using `anydata.convertCollection()`
435436
- To compare object type you need to convert it to `anydata` by using `anydata.convertObject()`
@@ -445,45 +446,139 @@ utPLSQL offers advanced data-comparison options, for comparing compound data-typ
445446

446447
For details on available options and how to use them, read the [advanced data comparison](advanced_data_comparison.md) guide.
447448

449+
### Diff functionality for compound data-types
448450

449-
### Compound data comparison examples
451+
When comparing compound data, utPLSQL will determine diff between expected and actual data.
452+
The diff includes:
453+
- differences in column names, column positions and column data-type for cursor data
454+
- only data in columns/rows that differ
450455

451-
Cursor comparison.
456+
The diff aims to make it easier to identify what is not expected in the actual data.
457+
458+
Consider the following expected cursor data
459+
460+
| ID (NUMBER)| FIRST_NAME (VARCHAR2) | LAST_NAME (VARCHAR2) | SALARY (NUMBER) |
461+
|:----------:|:----------------------:|:----------------------:|:---------------:|
462+
| 1 | JACK | SPARROW | 10000 |
463+
| 2 | LUKE | SKYWALKER | 1000 |
464+
| 3 | TONY | STARK | 1000000 |
465+
466+
And the actual cursor data:
467+
468+
|~~GENDER (VARCHAR2)~~| FIRST_NAME (VARCHAR2) | LAST_NAME (VARCHAR2) | SALARY *(VARCHAR2)* | *ID* (NUMBER) |
469+
|:-------------------:|:---------------------:|:--------------------:|:-------------------:|:-------------:|
470+
| M | JACK | SPARROW | **25000** | 1 |
471+
| M | TONY | STARK | 1000000 | 3 |
472+
| **F** | **JESSICA** | **JONES** | **2345** | **4** |
473+
| M | LUKE | SKYWALKER | 1000 | 2 |
474+
475+
476+
When considering the data-sets as ordered, there are following following differences:
477+
- column ID is misplaced (should be first column but is last)
478+
- column SALARY has data-type VARCHAR2 but should be NUMBER
479+
- column GENDER exists in actual but not in the expected (it ir an Extra column)
480+
- data in column SALARY for row number 1 in actual is not matching expected
481+
- row number 2 in actual (ID=3) is not matching expected
482+
- row number 3 in actual (ID=4) is not matching expected
483+
- row number 4 in actual (ID=2) is not expected in results (Extra row in actual)
484+
485+
utPLSQL will report all of the above differences in a readable format to help you identify what is not correct in compared data-set.
486+
487+
Below example illustrates, how utPLSQL will report such differences.
452488
```sql
453-
create or replace function get_user_tables return sys_refcursor is
454-
l_result sys_refcursor;
455-
begin
456-
open l_result for select d.* from user_tables d;
457-
return l_result;
489+
create or replace package test_cursor_compare as
490+
--%suite
491+
492+
--%test
493+
procedure do_test;
458494
end;
459495
/
460496

461-
create or replace package test_cursor_example is
462-
--%suite(example)
463-
464-
--%test(compare cursors)
465-
procedure test_cursors;
466-
end;
467-
/
468-
create or replace package body test_cursor_example is
469-
procedure test_cursors is
497+
create or replace package body test_cursor_compare as
498+
procedure do_test is
499+
l_actual sys_refcursor;
470500
l_expected sys_refcursor;
471501
begin
472-
--Arrange
473-
open l_expected for select d.* from user_tables d;
474-
--Act/Assert
475-
ut.expect( get_user_tables() ).to_equal( l_expected );
502+
open l_expected for
503+
select 1 as ID, 'JACK' as FIRST_NAME, 'SPARROW' AS LAST_NAME, 10000 AS SALARY
504+
from dual union all
505+
select 2 as ID, 'LUKE' as FIRST_NAME, 'SKYWALKER' AS LAST_NAME, 1000 AS SALARY
506+
from dual union all
507+
select 3 as ID, 'TONY' as FIRST_NAME, 'STARK' AS LAST_NAME, 100000 AS SALARY
508+
from dual;
509+
open l_actual for
510+
select 'M' AS GENDER, 'JACK' as FIRST_NAME, 'SPARROW' AS LAST_NAME, 1 as ID, '25000' AS SALARY
511+
from dual union all
512+
select 'M' AS GENDER, 'TONY' as FIRST_NAME, 'STARK' AS LAST_NAME, 3 as ID, '100000' AS SALARY
513+
from dual union all
514+
select 'F' AS GENDER, 'JESSICA' as FIRST_NAME, 'JONES' AS LAST_NAME, 4 as ID, '2345' AS SALARY
515+
from dual union all
516+
select 'M' AS GENDER, 'LUKE' as FIRST_NAME, 'SKYWALKER' AS LAST_NAME, 2 as ID, '1000' AS SALARY
517+
from dual;
518+
ut.expect(l_actual).to_equal(l_expected);
476519
end;
477520
end;
478521
/
479-
begin
480-
ut.run('test_cursor_example');
481-
end;
482-
/
483-
drop package test_cursor_example;
484-
drop function get_user_tables;
485522
```
486523

524+
When the test package is executed using:
525+
526+
```sql
527+
set serverout on
528+
exec ut.run('test_cursor_compare');
529+
```
530+
We get the following report:
531+
```
532+
test_cursor_compare
533+
do_test [.052 sec] (FAILED - 1)
534+
535+
Failures:
536+
537+
1) do_test
538+
Actual: refcursor [ count = 4 ] was expected to equal: refcursor [ count = 3 ]
539+
Diff:
540+
Columns:
541+
Column <ID> is misplaced. Expected position: 1, actual position: 4.
542+
Column <SALARY> data-type is invalid. Expected: NUMBER, actual: VARCHAR2.
543+
Column <GENDER> [position: 1, data-type: CHAR] is not expected in results.
544+
Rows: [ 4 differences ]
545+
Row No. 1 - Actual: <SALARY>25000</SALARY>
546+
Row No. 1 - Expected: <SALARY>10000</SALARY>
547+
Row No. 2 - Actual: <FIRST_NAME>TONY</FIRST_NAME><LAST_NAME>STARK</LAST_NAME><ID>3</ID><SALARY>100000</SALARY>
548+
Row No. 2 - Expected: <ID>2</ID><FIRST_NAME>LUKE</FIRST_NAME><LAST_NAME>SKYWALKER</LAST_NAME><SALARY>1000</SALARY>
549+
Row No. 3 - Actual: <FIRST_NAME>JESSICA</FIRST_NAME><LAST_NAME>JONES</LAST_NAME><ID>4</ID><SALARY>2345</SALARY>
550+
Row No. 3 - Expected: <ID>3</ID><FIRST_NAME>TONY</FIRST_NAME><LAST_NAME>STARK</LAST_NAME><SALARY>100000</SALARY>
551+
Row No. 4 - Extra: <GENDER>M</GENDER><FIRST_NAME>LUKE</FIRST_NAME><LAST_NAME>SKYWALKER</LAST_NAME><ID>2</ID><SALARY>1000</SALARY>
552+
at "UT3.TEST_CURSOR_COMPARE", line 22 ut.expect(l_actual).to_equal(l_expected);
553+
554+
555+
Finished in .053553 seconds
556+
1 tests, 1 failed, 0 errored, 0 disabled, 0 warning(s)
557+
```
558+
559+
utPLSQL identifies and reports on columns:
560+
- column misplacement
561+
- column data-type mismatch
562+
- extra/missing columns
563+
564+
When comparing rows utPLSQL:
565+
- reports only mismatched columns, when rows match
566+
- reports columns existing in both data-sets when whole row is not matching
567+
- reports whole extra (not expected) row from actual, when actual has extra rows
568+
- reports whole missing (expected) row from expected, when expected has extra rows
569+
570+
571+
### Object and collection data-type comparison examples
572+
573+
When comparing object type to object type or collection to collection, utPLSQL will check:
574+
- if data-types match
575+
- id data in the compared objects/collections are the same.
576+
577+
The diff functionality for objects and collections is similar to diff on cursors.
578+
When diffing objects/collections however, utPLSQL will not check attribute names and data-types.
579+
580+
Below examples demonstrate how to compare object and collection data-types.
581+
487582
Object type comparison.
488583
```sql
489584
create type department as object(name varchar2(30))

source/core/ut_utils.pkb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,8 +414,8 @@ procedure append_to_clob(a_src_clob in out nocopy clob, a_clob_table t_clob_tab,
414414

415415
procedure cleanup_temp_tables is
416416
begin
417-
execute immediate 'delete from ut_data_set_tmp';
418-
execute immediate 'delete from ut_data_set_diff_tmp';
417+
execute immediate 'delete from ut_compound_data_tmp';
418+
execute immediate 'delete from ut_compound_data_diff_tmp';
419419
end;
420420

421421
function to_version(a_version_no varchar2) return t_version is

source/create_synonyms_and_grants_for_public.sql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ grant execute on &&ut3_owner..ut_file_mapping to public;
6868
grant execute on &&ut3_owner..ut_file_mapper to public;
6969
grant execute on &&ut3_owner..ut_key_value_pairs to public;
7070
grant execute on &&ut3_owner..ut_key_value_pair to public;
71-
grant select, insert, delete on &&ut3_owner..ut_data_set_tmp to public;
72-
grant select, insert, delete on &&ut3_owner..ut_data_set_diff_tmp to public;
71+
grant select, insert, delete on &&ut3_owner..ut_compound_data_tmp to public;
72+
grant select, insert, delete on &&ut3_owner..ut_compound_data_diff_tmp to public;
7373
grant execute on &&ut3_owner..ut_sonar_test_reporter to public;
7474
grant execute on &&ut3_owner..ut_annotations to public;
7575
grant execute on &&ut3_owner..ut_annotation to public;
@@ -123,6 +123,6 @@ create public synonym ut_file_mapping for &&ut3_owner..ut_file_mapping;
123123
create public synonym ut_file_mapper for &&ut3_owner..ut_file_mapper;
124124
create public synonym ut_key_value_pairs for &&ut3_owner..ut_key_value_pairs;
125125
create public synonym ut_key_value_pair for &&ut3_owner..ut_key_value_pair;
126-
create public synonym ut_data_set_tmp for &&ut3_owner..ut_data_set_tmp;
127-
create public synonym ut_data_set_diff_tmp for &&ut3_owner..ut_data_set_diff_tmp;
126+
create public synonym ut_compound_data_tmp for &&ut3_owner..ut_compound_data_tmp;
127+
create public synonym ut_compound_data_diff_tmp for &&ut3_owner..ut_compound_data_diff_tmp;
128128
create public synonym ut_sonar_test_reporter for &&ut3_owner..ut_sonar_test_reporter;

source/create_synonyms_and_grants_for_user.sql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ grant execute on &&ut3_owner..ut_file_mapping to &ut3_user;
8888
grant execute on &&ut3_owner..ut_file_mapper to &ut3_user;
8989
grant execute on &&ut3_owner..ut_key_value_pairs to &ut3_user;
9090
grant execute on &&ut3_owner..ut_key_value_pair to &ut3_user;
91-
grant select, insert, delete on &&ut3_owner..ut_data_set_tmp to &ut3_user;
92-
grant select, insert, delete on &&ut3_owner..ut_data_set_diff_tmp to &ut3_user;
91+
grant select, insert, delete on &&ut3_owner..ut_compound_data_tmp to &ut3_user;
92+
grant select, insert, delete on &&ut3_owner..ut_compound_data_diff_tmp to &ut3_user;
9393
grant execute on &&ut3_owner..ut_sonar_test_reporter to &ut3_user;
9494
grant execute on &&ut3_owner..ut_annotations to &ut3_user;
9595
grant execute on &&ut3_owner..ut_annotation to &ut3_user;
@@ -142,6 +142,6 @@ create or replace synonym &ut3_user..ut_file_mapping for &&ut3_owner..ut_file_ma
142142
create or replace synonym &ut3_user..ut_file_mapper for &&ut3_owner..ut_file_mapper;
143143
create or replace synonym &ut3_user..ut_key_value_pairs for &&ut3_owner..ut_key_value_pairs;
144144
create or replace synonym &ut3_user..ut_key_value_pair for &&ut3_owner..ut_key_value_pair;
145-
create or replace synonym &ut3_user..ut_data_set_tmp for &&ut3_owner..ut_cursor_data;
146-
create or replace synonym &ut3_user..ut_data_set_diff_tmp for &&ut3_owner..ut_data_set_diff_tmp;
145+
create or replace synonym &ut3_user..ut_compound_data_tmp for &&ut3_owner..ut_cursor_data;
146+
create or replace synonym &ut3_user..ut_compound_data_diff_tmp for &&ut3_owner..ut_compound_data_diff_tmp;
147147
create or replace synonym &ut3_user..ut_sonar_test_reporter for &&ut3_owner..ut_sonar_test_reporter;

source/expectations/data_values/ut_data_set_diff_tmp.sql renamed to source/expectations/data_values/ut_compound_data_diff_tmp.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
create global temporary table ut_data_set_diff_tmp(
1+
create global temporary table ut_compound_data_diff_tmp(
22
/*
33
utPLSQL - Version 3
44
Copyright 2016 - 2017 utPLSQL Project
@@ -14,5 +14,5 @@ create global temporary table ut_data_set_diff_tmp(
1414
*/
1515
diff_id raw(16),
1616
item_no integer,
17-
constraint ut_data_set_diff_tmp_pk primary key(diff_id,item_no)
17+
constraint ut_compound_data_diff_tmp_pk primary key(diff_id,item_no)
1818
) on commit preserve rows;

0 commit comments

Comments
 (0)