Testing query output with cursor comparison¶
utPLSQL can compare results of two queries in single line call to ut.expect(). Within your test, define the SQL queries as SYS_REFCURSOR variables.
Open one cursor with the expected rows (usually a static fixture data), open a second cursor with the tested SQL query, and pass both to ut.expect().
declare
l_expected sys_refcursor;
l_actual sys_refcursor;
begin
open l_expected for
select 1 as id, 'Alice' as name from dual union all
select 2 as id, 'Bob' as name from dual;
open l_actual for
select id, name
from customers
where status = 'ACTIVE'
order by id;
ut.expect(l_actual).to_equal(l_expected);
end;
How failure output works¶
When rows differ, utPLSQL reports which rows were expected but absent, which were present but unexpected, and which differed in column values. Rows are also considered different if column order is different or if datatype of a column is different.
Matching options¶
Ordered comparison (default): rows must appear in the same order in both cursors.
Unordered comparison: row order is ignored.
Key-based matching: when order differs between cursors, use join_by() to match rows on a key column rather than on row position.
Unordered columns (uc): when columns are allowed to have different order between expected and actual dataset - column matching by name not by position in result set
ut.expect(l_actual).to_equal(l_expected).unordered_columns();
ut.expect(l_actual).to_equal(l_expected).uc();
Include/exclude columns: when the tested cursor has columns that you do not wish or cannot compare in the test
ut.expect(l_actual).to_equal(l_expected).exclude('CREATE_DATE,LAST_EDIT_DATE');
ut.expect(l_actual).to_equal(l_expected).include('NAME,DESCRIPTION');
Notes on fixture design¶
Cursor comparison is most valuable and readable when fixture data is minimal and explicit.
Use UNION ALL selects from DUAL for expected data rather than querying from tables that other tests might modify.
This keeps expected values readable and the test self-contained.