From 6bf2c8e39e2b43e4132c3555d35cd27d10130485 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sun, 18 Nov 2018 00:26:21 +0000 Subject: [PATCH 1/2] Added ability to control rollback bekavior of a test-run. --- source/api/ut.pkb | 91 +++++++++++-------- source/api/ut.pks | 18 ++-- source/api/ut_runner.pkb | 18 ++-- source/api/ut_runner.pks | 4 +- source/core/types/ut_logical_suite.tpb | 6 +- source/core/types/ut_logical_suite.tps | 2 +- source/core/types/ut_run.tpb | 8 ++ source/core/types/ut_run.tps | 1 + source/core/types/ut_suite_item.tpb | 4 +- source/core/types/ut_suite_item.tps | 2 +- test/api/test_ut_run.pkb | 116 +++++++++++++++++++++++++ test/api/test_ut_run.pks | 15 ++++ 12 files changed, 231 insertions(+), 54 deletions(-) diff --git a/source/api/ut.pkb b/source/api/ut.pkb index bd241ce13..bb599704e 100644 --- a/source/api/ut.pkb +++ b/source/api/ut.pkb @@ -18,6 +18,7 @@ create or replace package body ut is */ g_nls_date_format varchar2(4000); + gc_fail_on_errors constant boolean := false; function version return varchar2 is begin @@ -117,7 +118,6 @@ create or replace package body ut is a_client_character_set varchar2 := null ) is pragma autonomous_transaction; - c_fail_on_errors constant boolean := false; begin a_reporter := coalesce(a_reporter,ut_documentation_reporter()); ut_runner.run( @@ -129,7 +129,7 @@ create or replace package body ut is a_test_file_mappings, a_include_objects, a_exclude_objects, - c_fail_on_errors, + gc_fail_on_errors, a_client_character_set ); rollback; @@ -147,7 +147,6 @@ create or replace package body ut is a_client_character_set varchar2 := null ) is pragma autonomous_transaction; - c_fail_on_errors constant boolean := false; begin a_reporter := coalesce(a_reporter,ut_documentation_reporter()); ut_runner.run( @@ -159,7 +158,7 @@ create or replace package body ut is ut_file_mapper.build_file_mappings(a_test_files), a_include_objects, a_exclude_objects, - c_fail_on_errors, + gc_fail_on_errors, a_client_character_set ); rollback; @@ -406,21 +405,39 @@ create or replace package body ut is a_test_file_mappings ut_file_mappings := null, a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, - a_client_character_set varchar2 := null + a_client_character_set varchar2 := null, + a_force_manual_rollback boolean := false ) is l_reporter ut_reporter_base := a_reporter; begin - run_autonomous( - a_paths, - l_reporter, - ut_utils.boolean_to_int(a_color_console), - a_coverage_schemes, - a_source_file_mappings, - a_test_file_mappings, - a_include_objects, - a_exclude_objects, - a_client_character_set - ); + if a_force_manual_rollback then + l_reporter := coalesce(l_reporter,ut_documentation_reporter()); + ut_runner.run( + a_paths, + ut_reporters(l_reporter), + a_color_console, + a_coverage_schemes, + a_source_file_mappings, + a_test_file_mappings, + a_include_objects, + a_exclude_objects, + gc_fail_on_errors, + a_client_character_set, + a_force_manual_rollback + ); + else + run_autonomous( + a_paths, + l_reporter, + ut_utils.boolean_to_int(a_color_console), + a_coverage_schemes, + a_source_file_mappings, + a_test_file_mappings, + a_include_objects, + a_exclude_objects, + a_client_character_set + ); + end if; if l_reporter is of (ut_output_reporter_base) then treat(l_reporter as ut_output_reporter_base).lines_to_dbms_output(); end if; @@ -436,25 +453,23 @@ create or replace package body ut is a_test_files ut_varchar2_list, a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, - a_client_character_set varchar2 := null + a_client_character_set varchar2 := null, + a_force_manual_rollback boolean := false ) is l_reporter ut_reporter_base := a_reporter; begin - run_autonomous( + ut.run( a_paths, l_reporter, - ut_utils.boolean_to_int(a_color_console), + a_color_console, a_coverage_schemes, - a_source_files, - a_test_files, + ut_file_mapper.build_file_mappings(a_source_files), + ut_file_mapper.build_file_mappings(a_test_files), a_include_objects, a_exclude_objects, - a_client_character_set + a_client_character_set, + a_force_manual_rollback ); - if l_reporter is of (ut_output_reporter_base) then - treat(l_reporter as ut_output_reporter_base).lines_to_dbms_output(); - end if; - raise_if_packages_invalidated(); end; procedure run( @@ -465,7 +480,8 @@ create or replace package body ut is a_test_file_mappings ut_file_mappings := null, a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, - a_client_character_set varchar2 := null + a_client_character_set varchar2 := null, + a_force_manual_rollback boolean := false ) is begin ut.run( @@ -477,7 +493,8 @@ create or replace package body ut is a_test_file_mappings, a_include_objects, a_exclude_objects, - a_client_character_set + a_client_character_set, + a_force_manual_rollback ); end; @@ -489,7 +506,8 @@ create or replace package body ut is a_test_files ut_varchar2_list, a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, - a_client_character_set varchar2 := null + a_client_character_set varchar2 := null, + a_force_manual_rollback boolean := false ) is begin ut.run( @@ -501,7 +519,8 @@ create or replace package body ut is a_test_files, a_include_objects, a_exclude_objects, - a_client_character_set + a_client_character_set, + a_force_manual_rollback ); end; @@ -514,7 +533,8 @@ create or replace package body ut is a_test_file_mappings ut_file_mappings := null, a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, - a_client_character_set varchar2 := null + a_client_character_set varchar2 := null, + a_force_manual_rollback boolean := false ) is begin ut.run( @@ -526,7 +546,8 @@ create or replace package body ut is a_test_file_mappings, a_include_objects, a_exclude_objects, - a_client_character_set + a_client_character_set, + a_force_manual_rollback ); end; @@ -539,7 +560,8 @@ create or replace package body ut is a_test_files ut_varchar2_list, a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, - a_client_character_set varchar2 := null + a_client_character_set varchar2 := null, + a_force_manual_rollback boolean := false ) is begin ut.run( @@ -551,7 +573,8 @@ create or replace package body ut is a_test_files, a_include_objects, a_exclude_objects, - a_client_character_set + a_client_character_set, + a_force_manual_rollback ); end; diff --git a/source/api/ut.pks b/source/api/ut.pks index 59c709a33..25c9e6493 100644 --- a/source/api/ut.pks +++ b/source/api/ut.pks @@ -125,7 +125,8 @@ create or replace package ut authid current_user as a_test_file_mappings ut_file_mappings := null, a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, - a_client_character_set varchar2 := null + a_client_character_set varchar2 := null, + a_force_manual_rollback boolean := false ); procedure run( @@ -136,7 +137,8 @@ create or replace package ut authid current_user as a_test_files ut_varchar2_list, a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, - a_client_character_set varchar2 := null + a_client_character_set varchar2 := null, + a_force_manual_rollback boolean := false ); procedure run( @@ -148,7 +150,8 @@ create or replace package ut authid current_user as a_test_file_mappings ut_file_mappings := null, a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, - a_client_character_set varchar2 := null + a_client_character_set varchar2 := null, + a_force_manual_rollback boolean := false ); procedure run( @@ -160,7 +163,8 @@ create or replace package ut authid current_user as a_test_files ut_varchar2_list, a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, - a_client_character_set varchar2 := null + a_client_character_set varchar2 := null, + a_force_manual_rollback boolean := false ); procedure run( @@ -172,7 +176,8 @@ create or replace package ut authid current_user as a_test_file_mappings ut_file_mappings := null, a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, - a_client_character_set varchar2 := null + a_client_character_set varchar2 := null, + a_force_manual_rollback boolean := false ); procedure run( @@ -184,7 +189,8 @@ create or replace package ut authid current_user as a_test_files ut_varchar2_list, a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, - a_client_character_set varchar2 := null + a_client_character_set varchar2 := null, + a_force_manual_rollback boolean := false ); /** diff --git a/source/api/ut_runner.pkb b/source/api/ut_runner.pkb index c04fa5112..a4c2b3dc6 100644 --- a/source/api/ut_runner.pkb +++ b/source/api/ut_runner.pkb @@ -42,13 +42,16 @@ create or replace package body ut_runner is return l_result; end; - procedure finish_run(a_run ut_run) is + procedure finish_run(a_run ut_run, a_force_manual_rollback boolean) is begin ut_utils.cleanup_temp_tables; ut_event_manager.trigger_event(ut_utils.gc_finalize, a_run); ut_metadata.reset_source_definition_cache; ut_utils.read_cache_to_dbms_output(); ut_coverage_helper.cleanup_tmp_table(); + if not a_force_manual_rollback then + rollback; + end if; end; @@ -83,7 +86,8 @@ create or replace package body ut_runner is a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, a_fail_on_errors boolean := false, - a_client_character_set varchar2 := null + a_client_character_set varchar2 := null, + a_force_manual_rollback boolean := false ) is l_run ut_run; l_coverage_schema_names ut_varchar2_rows; @@ -137,16 +141,18 @@ create or replace package body ut_runner is set(a_test_file_mappings), a_client_character_set ); + if a_force_manual_rollback then + l_run.set_rollback_type(ut_utils.gc_rollback_manual, a_force=>true); + end if; + l_run.do_execute(); - finish_run(l_run); - rollback; + finish_run(l_run, a_force_manual_rollback); exception when others then - finish_run(l_run); + finish_run(l_run, a_force_manual_rollback); dbms_output.put_line(dbms_utility.format_error_backtrace); dbms_output.put_line(dbms_utility.format_error_stack); - rollback; raise; end; if a_fail_on_errors and l_run.result in (ut_utils.gc_failure, ut_utils.gc_error) then diff --git a/source/api/ut_runner.pks b/source/api/ut_runner.pks index 45b8cb642..b1511c623 100644 --- a/source/api/ut_runner.pks +++ b/source/api/ut_runner.pks @@ -43,6 +43,7 @@ create or replace package ut_runner authid current_user is * @param a_include_objects list of database objects (in format 'owner.name') that coverage should be reported on * @param a_exclude_objects list of database objects (in format 'owner.name') that coverage should be skipped for * @param a_fail_on_errors true/false - should an exception be thrown when tests are completed with failures/errors + * @param a_force_manual_rollback true/false - should the transaction control be forced to --%rollback(manual) and no rollback issued at the end of the run * * @example * Parameter `a_paths` accepts values of the following formats: @@ -65,7 +66,8 @@ create or replace package ut_runner authid current_user is a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, a_fail_on_errors boolean := false, - a_client_character_set varchar2 := null + a_client_character_set varchar2 := null, + a_force_manual_rollback boolean := false ); /** diff --git a/source/core/types/ut_logical_suite.tpb b/source/core/types/ut_logical_suite.tpb index 0443bf105..97c089ecb 100644 --- a/source/core/types/ut_logical_suite.tpb +++ b/source/core/types/ut_logical_suite.tpb @@ -51,11 +51,11 @@ create or replace type body ut_logical_suite as self.calc_execution_result(); end; - overriding member procedure set_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer) is + overriding member procedure set_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer, a_force boolean := false) is begin - self.rollback_type := coalesce(self.rollback_type, a_rollback_type); + self.rollback_type := case when a_force then a_rollback_type else coalesce(self.rollback_type, a_rollback_type) end; for i in 1 .. self.items.count loop - self.items(i).set_rollback_type(self.rollback_type); + self.items(i).set_rollback_type(self.rollback_type, a_force); end loop; end; diff --git a/source/core/types/ut_logical_suite.tps b/source/core/types/ut_logical_suite.tps index 85bb80870..e9ee20a93 100644 --- a/source/core/types/ut_logical_suite.tps +++ b/source/core/types/ut_logical_suite.tps @@ -27,7 +27,7 @@ create or replace type ut_logical_suite under ut_suite_item ( member function is_valid(self in out nocopy ut_logical_suite) return boolean, member procedure add_item(self in out nocopy ut_logical_suite, a_item ut_suite_item), overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite), - overriding member procedure set_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer), + overriding member procedure set_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer, a_force boolean := false), overriding member function do_execute(self in out nocopy ut_logical_suite) return boolean, overriding member procedure calc_execution_result(self in out nocopy ut_logical_suite), overriding member procedure mark_as_errored(self in out nocopy ut_logical_suite, a_error_stack_trace varchar2), diff --git a/source/core/types/ut_run.tpb b/source/core/types/ut_run.tpb index 0db9f38c0..d77999a57 100644 --- a/source/core/types/ut_run.tpb +++ b/source/core/types/ut_run.tpb @@ -75,6 +75,14 @@ create or replace type body ut_run as return l_completed_without_errors; end; + overriding member procedure set_rollback_type(self in out nocopy ut_run, a_rollback_type integer, a_force boolean := false) is + begin + self.rollback_type := case when a_force then a_rollback_type else coalesce(self.rollback_type, a_rollback_type) end; + for i in 1 .. self.items.count loop + self.items(i).set_rollback_type(self.rollback_type, a_force); + end loop; + end; + overriding member procedure calc_execution_result(self in out nocopy ut_run) is l_result integer(1); begin diff --git a/source/core/types/ut_run.tps b/source/core/types/ut_run.tps index 5dc8e0399..e4309e126 100644 --- a/source/core/types/ut_run.tps +++ b/source/core/types/ut_run.tps @@ -37,6 +37,7 @@ create or replace type ut_run under ut_suite_item ( ) return self as result, overriding member procedure mark_as_skipped(self in out nocopy ut_run), overriding member function do_execute(self in out nocopy ut_run) return boolean, + overriding member procedure set_rollback_type(self in out nocopy ut_run, a_rollback_type integer, a_force boolean := false), overriding member procedure calc_execution_result(self in out nocopy ut_run), overriding member procedure mark_as_errored(self in out nocopy ut_run, a_error_stack_trace varchar2), overriding member function get_error_stack_traces return ut_varchar2_list, diff --git a/source/core/types/ut_suite_item.tpb b/source/core/types/ut_suite_item.tpb index a3319a4d8..0d1faffa3 100644 --- a/source/core/types/ut_suite_item.tpb +++ b/source/core/types/ut_suite_item.tpb @@ -37,9 +37,9 @@ create or replace type body ut_suite_item as return ut_utils.int_to_boolean(self.disabled_flag); end; - member procedure set_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer) is + member procedure set_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer, a_force boolean := false) is begin - self.rollback_type := coalesce(self.rollback_type, a_rollback_type); + self.rollback_type := case when a_force then a_rollback_type else coalesce(self.rollback_type, a_rollback_type) end; end; member function get_rollback_type return integer is diff --git a/source/core/types/ut_suite_item.tps b/source/core/types/ut_suite_item.tps index 52b6a2a19..f0821fbb8 100644 --- a/source/core/types/ut_suite_item.tps +++ b/source/core/types/ut_suite_item.tps @@ -56,7 +56,7 @@ create or replace type ut_suite_item force under ut_event_item ( member procedure set_disabled_flag(self in out nocopy ut_suite_item, a_disabled_flag boolean), member function get_disabled_flag return boolean, not instantiable member procedure mark_as_skipped(self in out nocopy ut_suite_item), - member procedure set_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer), + member procedure set_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer, a_force boolean := false), member function get_rollback_type return integer, member function create_savepoint_if_needed return varchar2, member procedure rollback_to_savepoint(self in out nocopy ut_suite_item, a_savepoint varchar2), diff --git a/test/api/test_ut_run.pkb b/test/api/test_ut_run.pkb index dae7e175a..50612e53c 100644 --- a/test/api/test_ut_run.pkb +++ b/test/api/test_ut_run.pkb @@ -355,6 +355,122 @@ create or replace package body test_ut_run is ut.expect( l_results ).to_be_like( '%test_package_1%test_package_2%test_package_3%' ); end; + procedure transaction_setup is + pragma autonomous_transaction; + begin + execute immediate 'create table transaction_test_table(message varchar2(100))'; + execute immediate 'create or replace package test_transaction is + --%suite + + --%test + procedure insert_row; + + --%test + procedure insert_and_raise; + end; + '; + execute immediate 'create or replace package body test_transaction is + procedure insert_row is + begin + insert into transaction_test_table values (''2 - inside the test_transaction.insert_row test''); + end; + procedure insert_and_raise is + begin + insert into transaction_test_table values (''2 - inside the test_transaction.insert_row test''); + raise no_data_found; + end; + end; + '; + + end; + + procedure transaction_cleanup is + pragma autonomous_transaction; + begin + begin + execute immediate 'drop table transaction_test_table'; + exception + when others then null; + end; + begin + execute immediate 'drop pacakge test_transaction'; + exception + when others then null; + end; + end; + + procedure run_proc_keep_test_data is + l_expected sys_refcursor; + l_actual sys_refcursor; + l_results clob; + begin + --Arrange + execute immediate ' + insert into transaction_test_table values (''1 - inside the test_ut_run.run_proc_keep_test_changes test'')'; + + --Act + ut3.ut.run('test_transaction.insert_row', a_force_manual_rollback => true); + l_results := get_dbms_output_as_clob(); + + --Assert + open l_expected for + select '1 - inside the test_ut_run.run_proc_keep_test_changes test' as message from dual + union all + select '2 - inside the test_transaction.insert_row test' from dual + order by 1; + + open l_actual for 'select * from transaction_test_table order by 1'; + + ut.expect( l_actual ).to_equal(l_expected); + end; + + procedure run_proc_keep_test_data_raise is + l_expected sys_refcursor; + l_actual sys_refcursor; + l_results clob; + begin + --Arrange + execute immediate ' + insert into transaction_test_table values (''1 - inside the test_ut_run.run_proc_keep_test_changes test'')'; + + --Act + ut3.ut.run('test_transaction.insert_and_raise', a_force_manual_rollback => true); + l_results := get_dbms_output_as_clob(); + + --Assert + open l_expected for + select '1 - inside the test_ut_run.run_proc_keep_test_changes test' as message from dual + union all + select '2 - inside the test_transaction.insert_row test' from dual + order by 1; + + open l_actual for 'select * from transaction_test_table order by 1'; + + ut.expect( l_actual ).to_equal(l_expected); + end; + + procedure run_proc_discard_test_data is + l_expected sys_refcursor; + l_actual sys_refcursor; + l_results clob; + begin + --Arrange + execute immediate ' + insert into transaction_test_table values (''1 - inside the test_ut_run.run_proc_keep_test_changes test'')'; + + --Act + ut3.ut.run('test_transaction.insert_row'); + l_results := get_dbms_output_as_clob(); + + --Assert + open l_expected for + select '1 - inside the test_ut_run.run_proc_keep_test_changes test' as message from dual; + + open l_actual for 'select * from transaction_test_table order by 1'; + + ut.expect( l_actual ).to_equal(l_expected); + end; + procedure run_func_no_params is l_results ut3.ut_varchar2_list; begin diff --git a/test/api/test_ut_run.pks b/test/api/test_ut_run.pks index cfc06c80d..b5fa8bd69 100644 --- a/test/api/test_ut_run.pks +++ b/test/api/test_ut_run.pks @@ -44,6 +44,21 @@ create or replace package test_ut_run is --%endcontext + --%context(run_proc_transaction_control) + + --%beforeall + procedure transaction_setup; + --%afterall + procedure transaction_cleanup; + --%test(Leaves transaction open and uncommitted with a_force_manual_rollback) + procedure run_proc_keep_test_data; + --%test(Leaves transaction open and uncommitted with a_force_manual_rollback with exceptions) + procedure run_proc_keep_test_data_raise; + --%test(Does not impact current transaction when ran without a_force_manual_rollback) + procedure run_proc_discard_test_data; + + --%endcontext + --%context(ut_run_function) --%displayname(ut.run() function options) From 4e3a654adde8c1fcb4c316d002ca3a4383d4bb98 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sun, 18 Nov 2018 00:40:04 +0000 Subject: [PATCH 2/2] Updated documentation. --- docs/userguide/running-unit-tests.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/userguide/running-unit-tests.md b/docs/userguide/running-unit-tests.md index 54116cf65..b67569b8f 100644 --- a/docs/userguide/running-unit-tests.md +++ b/docs/userguide/running-unit-tests.md @@ -131,6 +131,26 @@ Executes all tests from package _HR.TEST_APPLY_BONUS_ and provide outputs to DBM For details on build-in reporters look at [reporters documentation](reporters.md). +## Keeping uncommited data after test-run + +utPLSQL by default runs tests in autonomous transaction and performs automatic rollback to assure that tests do not impact one-another and do not have impact on the current session in your IDE. + +If you would like to keep your uncommited data persisted after running tests, you can do so by using `a_force_manual_rollback` flag. +Setting this flag to true has following side-effects: + +- test execution is done in current transaction - if while running tests commit or rollback is issued your current session data will get commited too. +- automatic rollback is forced to be disabled in test-run even if it was explicitly enabled by using annotation `--%rollback(manual) + +Example invocation: +```sql +begin + ut.run('hr.test_apply_bonus', a_force_manual_rollback => true); +end; +``` + + +This option is not anvailable when running tests using `ut.run` as a table function. + ## ut.run functions The `ut.run` functions provide exactly the same functionality as the `ut.run` procedures.