Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 82 additions & 41 deletions docs/userguide/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -1765,8 +1765,8 @@ Keep in mind that when your test runs as autonomous transaction it will not see
The `--%throws` annotation allows you to specify a list of exceptions as one of:

- number literals - example `--%throws(-20134)`
- variables of type `exception` defined in a package specification - example `--%throws(exc_pkg.c_exception_No_variable)`
- variables of type `number` defined in a package specification - example `--%throws(exc_pkg.c_some_exception)`
- variables of type `exception` defined in a package specification - example `--%throws(exc_pkg.c_exception_no_variable)`
- variables or constants of containing a valid negative exception number defined in a package specification - example `--%throws(exc_pkg.c_some_exception)`
- [predefined oracle exceptions](https://docs.oracle.com/en//database/oracle/oracle-database/19/lnpls/predefined-exceptions.html) - example `--%throws(no_data_found)`

The annotation is ignored when no valid arguments are provided. Examples of invalid annotations `--%throws()`,`--%throws`, `--%throws(abe, 723pf)`.
Expand All @@ -1783,23 +1783,27 @@ Please note that `NO_DATA_FOUND` exception is a special case in Oracle. To captu

Syntax: `--%throws( [[schema.]package.]exception [, ... ])`

So the exception name can be provided with or without the schema and package name. The package name is required only when the exception variable is located in another package. The schema name is required only when the exception variable is located in a pacakge in a nother schema.
The exception name can be provided with or without the schema and package name. The package name is required only when the exception variable is located in another package than the unit test package. The schema name is required only when the exception variable is located in a package in another schema.

Example:
```sql linenums="1"
create or replace package exc_pkg is
c_e_option1 constant number := -20200;
c_e_option2 constant varchar2(10) := '-20201';
c_e_option3 number := -20202;

e_option4 exception;
pragma exception_init(e_option4, -20203);
c_e_option3 integer := -20202;

e_initialized_exception exception;
pragma exception_init(e_initialized_exception, -20203);

e_uninitialized_exception exception;
end;
/

create or replace package example_pgk as

e_local_exception exception;
e_local_exception_num constant integer := -20123;

--%suite(Example Throws Annotation)

--%test(Throws one of the listed exceptions)
Expand Down Expand Up @@ -1830,10 +1834,22 @@ create or replace package example_pgk as
--%throws(exc_pkg.c_e_option3)
procedure raised_option3_exception;

--%test(Throws package exception option4)
--%throws(exc_pkg.e_option4)
procedure raised_option4_exception;
--%test(Throws exception associated with exception number)
--%throws(exc_pkg.e_initialized_exception)
procedure raised_initialized_exception;

--%test(Throws uninitialized exception)
--%throws(exc_pkg.e_uninitialized_exception)
procedure raised_uninitialized_exception;

--%test(Throws exception local to unit tests package)
--%throws(e_local_exception)
procedure raised_local_exception;

--%test(Throws exception local to unit tests package with exception number)
--%throws(e_local_exception_num)
procedure raised_local_exception_num;

--%test(Raise name exception)
--%throws(DUP_VAL_ON_INDEX)
procedure raise_named_exc;
Expand All @@ -1844,6 +1860,7 @@ create or replace package example_pgk as

end;
/

create or replace package body example_pgk is
procedure raised_one_listed_exception is
begin
Expand Down Expand Up @@ -1880,11 +1897,28 @@ create or replace package body example_pgk is
raise_application_error(exc_pkg.c_e_option3, 'Test error');
end;

procedure raised_option4_exception is
procedure raised_initialized_exception is
begin
raise exc_pkg.e_initialized_exception;
end;

procedure raised_uninitialized_exception is
begin
raise exc_pkg.e_option4;
raise exc_pkg.e_uninitialized_exception;
end;

procedure raised_local_exception is
pragma autonomous_transaction;
begin
raise e_local_exception;
end;

procedure raised_local_exception_num is
pragma autonomous_transaction;
begin
raise_application_error(e_local_exception_num, 'Test error');
end;

procedure raise_named_exc is
begin
raise DUP_VAL_ON_INDEX;
Expand All @@ -1903,48 +1937,55 @@ exec ut3.ut.run('example_pgk');
Running the test will give report:
```
Example Throws Annotation
Throws one of the listed exceptions [.002 sec]
Throws different exception than expected [.002 sec] (FAILED - 1)
Throws different exception than listed [.003 sec] (FAILED - 2)
Gives failure when an exception is expected and nothing is thrown [.002 sec] (FAILED - 3)
Throws package exception option1 [.003 sec]
Throws package exception option2 [.002 sec]
Throws package exception option3 [.002 sec]
Throws package exception option4 [.002 sec]
Raise name exception [.002 sec]
Invalid throws annotation [.002 sec]

Throws one of the listed exceptions [,026 sec]
Throws different exception than expected [,009 sec] (FAILED - 1)
Throws different exception than listed [,014 sec] (FAILED - 2)
Gives failure when an exception is expected and nothing is thrown [,016 sec] (FAILED - 3)
SUCCESS
Actual: 1 (number) was expected to equal: 1 (number)
Throws package exception option1 [,007 sec]
Throws package exception option2 [,008 sec]
Throws package exception option3 [,007 sec]
Throws exception associated with exception number [,006 sec]
Throws uninitialized exception [,006 sec]
Throws exception local to unit tests package [,009 sec]
Throws exception local to unit tests package with exception number [,011 sec]
Raise name exception [,006 sec]
Invalid throws annotation [,004 sec]

Failures:

1) raised_different_exception
Actual: -20143 was expected to equal: -20144
ORA-20143: Test error
ORA-06512: at "UT3.EXAMPLE_PGK", line 9
ORA-06512: at "UT3.EXAMPLE_PGK", line 9
ORA-06512: at line 6
ORA-06512: at "UT3_TESTER.EXAMPLE_PGK", line 9
ORA-06512: at "UT3_TESTER.EXAMPLE_PGK", line 9
ORA-06512: at line 7

2) raised_unlisted_exception
Actual: -20143 was expected to be one of: (-20144, -1, -20145)
ORA-20143: Test error
ORA-06512: at "UT3.EXAMPLE_PGK", line 14
ORA-06512: at "UT3.EXAMPLE_PGK", line 14
ORA-06512: at line 6
ORA-06512: at "UT3_TESTER.EXAMPLE_PGK", line 14
ORA-06512: at "UT3_TESTER.EXAMPLE_PGK", line 14
ORA-06512: at line 7

3) nothing_thrown
Expected one of exceptions (-20459, -20136, -20145) but nothing was raised.


Warnings:
1) example_pgk

1) example_pgk.raised_one_listed_exception
Invalid parameter value "bad" for "--%throws" annotation. Parameter ignored.
at "UT3.EXAMPLE_PGK.RAISED_ONE_LISTED_EXCEPTION", line 6
at package "UT3_TESTER.EXAMPLE_PGK.RAISED_ONE_LISTED_EXCEPTION", line 8

2) example_pgk
"--%throws" annotation requires a parameter. Annotation ignored.
at "UT3.EXAMPLE_PGK.BAD_THROWS_ANNOTATION", line 42

Finished in .025784 seconds
10 tests, 3 failed, 0 errored, 0 disabled, 2 warning(s)
at package "UT3_TESTER.EXAMPLE_PGK.BAD_THROWS_ANNOTATION", line 57

Finished in ,138276 seconds
13 tests, 3 failed, 0 errored, 0 disabled, 2 warning(s)

```

## Order of execution
Expand Down
54 changes: 40 additions & 14 deletions source/core/types/ut_executable.tpb
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,30 @@
return ut_metadata.form_name(l_owner_name, object_name, procedure_name);
end;

member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item) is
member procedure do_execute(
self in out nocopy ut_executable,
a_item in out nocopy ut_suite_item,
a_ignored_exception_names in ut_varchar2_rows := null,
a_ignored_exception_numbers in ut_varchar2_rows :=null)
is
l_completed_without_errors boolean;
begin
l_completed_without_errors := self.do_execute(a_item);
l_completed_without_errors := self.do_execute(a_item, a_ignored_exception_names, a_ignored_exception_numbers);
end do_execute;

member function do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item) return boolean is
l_statement varchar2(4000);
l_status number;
l_cursor_number number;
l_completed_without_errors boolean := true;
l_failed_with_invalid_pck boolean := true;
l_start_transaction_id varchar2(250);
l_end_transaction_id varchar2(250);
member function do_execute(
self in out nocopy ut_executable,
a_item in out nocopy ut_suite_item,
a_ignored_exception_names in ut_varchar2_rows := null,
a_ignored_exception_numbers in ut_varchar2_rows :=null)
return boolean is
l_statement varchar2(4000);
l_status number;
l_cursor_number number;
l_completed_without_errors boolean := true;
l_failed_with_invalid_pck boolean := true;
l_start_transaction_id varchar2(250);
l_end_transaction_id varchar2(250);

function is_defined return boolean is
l_result boolean := false;
Expand Down Expand Up @@ -111,19 +121,33 @@
if l_completed_without_errors then
l_statement :=
'declare' || chr(10) ||
' l_error_stack varchar2(32767);' || chr(10) ||
' l_error_backtrace varchar2(32767);' || chr(10) ||
' l_error_stack varchar2(32767);' || chr(10) ||
' l_error_backtrace varchar2(32767);' || chr(10) ||
' l_ignored_exception_detected integer := 0;' || chr(10) ||
'begin' || chr(10) ||
' begin' || chr(10) ||
' ' || self.form_name( a_skip_current_user_schema => true ) || ';' || chr(10) ||
' exception' || chr(10) ||
case when a_ignored_exception_names is not empty then
' when ' || ut_utils.table_to_clob( a_ignored_exception_names, ' or ' ) || ' then ' || chr(10) ||
' l_ignored_exception_detected := 1;'
end ||
' when others then ' || chr(10) ||
case when a_ignored_exception_numbers is not empty then
' if sqlcode in (' || ut_utils.table_to_clob( a_ignored_exception_numbers, ', ' ) || ') then ' || chr(10) ||
' l_ignored_exception_detected := 1; ' || chr(10) ||
' else ' || chr(10) ||
' l_error_stack := dbms_utility.format_error_stack;' || chr(10) ||
' l_error_backtrace := dbms_utility.format_error_backtrace;' || chr(10) ||
' end if;'
else
' l_error_stack := dbms_utility.format_error_stack;' || chr(10) ||
' l_error_backtrace := dbms_utility.format_error_backtrace;' || chr(10) ||
' --raise on ORA-04068, ORA-04061: existing state of packages has been discarded to avoid unrecoverable session exception' || chr(10) ||
' l_error_backtrace := dbms_utility.format_error_backtrace;'
end || chr(10) ||
' end;' || chr(10) ||
' :a_error_stack := l_error_stack;' || chr(10) ||
' :a_error_backtrace := l_error_backtrace;' || chr(10) ||
' :a_ignored_exception_detected := l_ignored_exception_detected;' || chr(10) ||
'end;';

ut_utils.debug_log('ut_executable.do_execute l_statement: ' || l_statement);
Expand All @@ -139,9 +163,11 @@
dbms_sql.parse(l_cursor_number, statement => l_statement, language_flag => dbms_sql.native);
dbms_sql.bind_variable(l_cursor_number, 'a_error_stack', to_char(null), 32767);
dbms_sql.bind_variable(l_cursor_number, 'a_error_backtrace', to_char(null), 32767);
dbms_sql.bind_variable(l_cursor_number, 'a_ignored_exception_detected', to_number(null));

Check warning on line 166 in source/core/types/ut_executable.tpb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Provide the format of the input value.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZ6sPAeZRD0Uh5IywUbU&open=AZ6sPAeZRD0Uh5IywUbU&pullRequest=1366
l_status := dbms_sql.execute(l_cursor_number);
dbms_sql.variable_value(l_cursor_number, 'a_error_stack', self.error_stack);
dbms_sql.variable_value(l_cursor_number, 'a_error_backtrace', self.error_backtrace);
dbms_sql.variable_value(l_cursor_number, 'a_ignored_exception_detected', self.ignored_exception_detected);

Check warning on line 170 in source/core/types/ut_executable.tpb

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer EXECUTE IMMEDIATE to DBMS_SQL's package calls.

See more on https://sonarcloud.io/project/issues?id=utPLSQL_utPLSQL&issues=AZ6sPAeZRD0Uh5IywUbV&open=AZ6sPAeZRD0Uh5IywUbV&pullRequest=1366
dbms_sql.close_cursor(l_cursor_number);
exception
when ut_utils.ex_invalid_package then
Expand Down
29 changes: 20 additions & 9 deletions source/core/types/ut_executable.tps
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,37 @@ create or replace noneditionable type ut_executable under ut_event_item(
/**
* The name of the event to be executed before and after the executable is invoked
*/
executable_type varchar2(250 char),
owner_name varchar2(250 char),
object_name varchar2(250 char),
procedure_name varchar2(250 char),
error_backtrace varchar2(4000),
error_stack varchar2(4000),
serveroutput clob,
executable_type varchar2(250 char),
owner_name varchar2(250 char),
object_name varchar2(250 char),
procedure_name varchar2(250 char),
error_backtrace varchar2(4000),
error_stack varchar2(4000),
ignored_exception_detected integer,
serveroutput clob,
/**
* Used for ordering of executables, as Oracle doesn not guarantee ordering of items in a nested table.
*/
seq_no integer,
constructor function ut_executable( self in out nocopy ut_executable, a_owner varchar2, a_package varchar2, a_procedure_name varchar2, a_executable_type varchar2) return self as result,
member function form_name(a_skip_current_user_schema boolean := false) return varchar2,
member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item),
member procedure do_execute(
self in out nocopy ut_executable,
a_item in out nocopy ut_suite_item,
a_ignored_exception_names in ut_varchar2_rows := null,
a_ignored_exception_numbers in ut_varchar2_rows := null
),
/**
* executes the defines executable
* returns true if executed without exceptions
* returns false if exceptions were raised
*/
member function do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item) return boolean,
member function do_execute(
self in out nocopy ut_executable,
a_item in out nocopy ut_suite_item,
a_ignored_exception_names in ut_varchar2_rows := null,
a_ignored_exception_numbers in ut_varchar2_rows :=null
) return boolean,
member function get_error_stack_trace return varchar2
) not final
/
Loading
Loading