diff --git a/README.md b/README.md index b1e64e0..473b9da 100644 --- a/README.md +++ b/README.md @@ -391,7 +391,7 @@ See [EXCEL2TXT.md](docs/EXCEL2TXT.md) for more info. ## Examples and Reference - Complete reference of classes and methods can be found in [REFERENCE.md](docs/REFERENCE.md). -- A simple example can be found in [/src/zmockup_loader_example.prog.abap](/src/zmockup_loader_example.prog.abap). +- Usage example can be found in [/src/zcl_mockup_loader_base_example.clas.abap](/src/zcl_mockup_loader_base_example.clas.abap). The class itself is an example of a base class for a larger set of tests with some convenience features (totally opinionated). - Also see unit tests - these are the most up-to-date examples - Have a look at the how-to section in the project [Wiki](../../wiki). - [Gitlab CI example](https://gitlab.com/atsybulsky/ut-monitor-example) - idea is to commit UTs sources as well as SMW0 object to repo and see the **text-based diffs** (which is not possible for binary objects obviously) diff --git a/src/zcl_mockup_loader_base_example.clas.abap b/src/zcl_mockup_loader_base_example.clas.abap new file mode 100644 index 0000000..076f52f --- /dev/null +++ b/src/zcl_mockup_loader_base_example.clas.abap @@ -0,0 +1,236 @@ +class ZCL_MOCKUP_LOADER_BASE_EXAMPLE definition + public + final + create public + for testing + duration short + risk level harmless . + + public section. + " THIS CLASS IN AN EXAMPLE + " It was marked as final intentionally (to avoid occasional subclassing), real class would be not final + + " To check the instaled version of the ML + constants gc_required_mockup_loader_ver type string value 'v2.2.2'. + + methods constructor. + class-methods class_constructor. + + protected section. + + class-data: + gi_ml type ref to zif_mockup_loader, " ML instance + gi_proxy_target type ref to zif_mockup_loader_stub_dummy, " Custom mock, when needed + gi_dao_stub type ref to zif_mockup_loader_stub_dummy. " Stub instance + + " This is shortcut to set up custom mock to pass calls to + class-methods _set_proxy_target + importing + ii_proxy_target type ref to zif_mockup_loader_stub_dummy. + + " Connects a method to a mock + " Results in stub_factory->connect call, but needed since the stub is autocreated later + class-methods _connect + importing + iv_str type string. + + " 2 methods to test any structure for ONLY and SKIP markers and report on them if found + " _has_only also returns abap_true if the marker is found -> the value can be then used in test case loop, see examples + class-methods _has_only_and_warn + importing + it_index type standard table + returning + value(rv_yes) type abap_bool. + class-methods _has_skip_and_warn + importing + it_index type standard table + returning + value(rv_yes) type abap_bool. + + " A shortcut to fire a warning message + class-methods _warn + importing + i_msg type string. + + private section. + + class-data go_stub_factory type ref to zcl_mockup_loader_stub_factory. + class-data gt_connections type string_table. + + methods _inject_db_mock. + + class-methods _setup_loader. + class-methods _generate_stub. + class-methods _connect_defaults. + class-methods _count_markers + importing + it_index type standard table + iv_field type abap_compname + returning + value(rv_count) type i. + class-methods _has_marker_and_warn + importing + it_index type standard table + iv_field type abap_compname + returning + value(rv_yes) type abap_bool. + +ENDCLASS. + + + +CLASS ZCL_MOCKUP_LOADER_BASE_EXAMPLE IMPLEMENTATION. + + + method class_constructor. + _setup_loader( ). + _connect_defaults( ). + endmethod. + + + method constructor. + if gi_dao_stub is not bound. "skip if generated + _generate_stub( ). + endif. + _inject_db_mock( ). + endmethod. + + + method _connect. + append iv_str to gt_connections. + endmethod. + + + method _connect_defaults. + + " Here should be connections which are common for all tested classes + " Such as settings, typical master data, etc. + " For exceptional cases these connections may be overriden in a specific test class if required + +* _connect( 'get_company_code -> ./cc [bukrs = i_bukrs]' ). +* _connect( 'get_document_type -> ./doct [blart = i_doctype]' ). +* _connect( 'get_partner_vbund -> ./tradep(vbund) [partner_no = i_partner]' ). +* _connect( 'get_tax_code_conditions -> ./~taxes [tax_code = i_mwskz]' ). +* _connect( 'get_tax_type -> ./taxes(mwart) [tax_code = i_mwskz]' ). + + endmethod. + + + method _count_markers. + field-symbols type any. + field-symbols type abap_bool. + + loop at it_index assigning . + assign component iv_field of structure to . + if sy-subrc <> 0. + return. " No field in the structure + endif. + if = abap_true. + rv_count = rv_count + 1. + endif. + endloop. + endmethod. + + + method _generate_stub. + + data lx type ref to cx_static_check. + + try. + + data lo_stub_factory type ref to zcl_mockup_loader_stub_factory. + data ld_stub_intf type ref to cl_abap_refdescr. + field-symbols like line of gt_connections. + + ld_stub_intf ?= cl_abap_typedescr=>describe_by_data( gi_dao_stub ). " Avoid hardcoding names when possible + + create object lo_stub_factory + exporting + ii_ml_instance = gi_ml + i_allow_overrides = abap_true + io_proxy_target = gi_proxy_target + i_interface_name = |{ ld_stub_intf->get_referenced_type( )->get_relative_name( ) }|. + + loop at gt_connections assigning . + lo_stub_factory->connect( ). + endloop. + + gi_dao_stub ?= lo_stub_factory->generate_stub( ). + + catch zcx_mockup_loader_error into lx. + cl_abap_unit_assert=>fail( lx->get_text( ) ). + endtry. + + endmethod. + + + method _has_marker_and_warn. + + data lv_count type i. + lv_count = _count_markers( + it_index = it_index + iv_field = iv_field ). + if lv_count > 0. + _warn( |test cases has { lv_count } { iv_field } markers, please check| ). + rv_yes = abap_true. + endif. + + endmethod. + + + method _has_only_and_warn. + rv_yes = _has_marker_and_warn( + it_index = it_index + iv_field = 'ONLY' ). + endmethod. + + + method _has_skip_and_warn. + rv_yes = _has_marker_and_warn( + it_index = it_index + iv_field = 'SKIP' ). + endmethod. + + + method _inject_db_mock. + " In real life this shoold be a global class which is a real commonly used DAO or some kind of DAO factory + " DAO = "data accessor object" + lcl_dao=>inject_instance( gi_dao_stub ). + endmethod. + + + method _setup_loader. + + data lx type ref to cx_static_check. + + try. + + gi_ml = zcl_mockup_loader=>create( + i_cache_timeout = 5 " may help in case of many test classes + i_encoding = zif_mockup_loader=>encoding_utf8 + i_amt_format = ' .' + i_path = 'ZMOCKUP_LOADER_EXAMPLE' ). + + if gi_ml->is_redirected( ) = abap_true. + _warn( 'Redirect is ON' ). + endif. + + catch zcx_mockup_loader_error into lx. + cl_abap_unit_assert=>fail( lx->get_text( ) ). + endtry. + + endmethod. + + + method _set_proxy_target. + gi_proxy_target = ii_proxy_target. + endmethod. + + + method _warn. + cl_abap_unit_assert=>fail( + msg = i_msg + level = if_aunit_constants=>tolerable + quit = if_aunit_constants=>no ). + endmethod. +ENDCLASS. diff --git a/src/zcl_mockup_loader_base_example.clas.locals_imp.abap b/src/zcl_mockup_loader_base_example.clas.locals_imp.abap new file mode 100644 index 0000000..a5c0563 --- /dev/null +++ b/src/zcl_mockup_loader_base_example.clas.locals_imp.abap @@ -0,0 +1,123 @@ +class lcl_dao definition final for testing. + public section. + class-methods get_instance + returning + value(ri_instance) type ref to zif_mockup_loader_stub_dummy. + class-methods inject_instance + importing + ii_instance type ref to zif_mockup_loader_stub_dummy. + interfaces zif_mockup_loader_stub_dummy. + + private section. + class-data mi_instance type ref to zif_mockup_loader_stub_dummy. +endclass. + +class lcl_dao implementation. + + method get_instance. + if mi_instance is not bound. + create object mi_instance type lcl_dao. + endif. + ri_instance = mi_instance. + endmethod. + + method inject_instance. + mi_instance = ii_instance. + endmethod. + + method zif_mockup_loader_stub_dummy~tab_return. + " Real selects here ... + select * from sflight into table r_tab where connid = i_connid. + endmethod. + +endclass. + +********************************************************************** +* SINGLETON SETTINGS CLASS (a sample way to get test env indicator) +********************************************************************** +class lcl_context definition final create private. + + public section. + data a_carrid type sflight-carrid read-only. " Indicates execution in test environment + data a_testenv type abap_bool read-only. + + class-methods get_instance + returning value(ro_instance) type ref to lcl_context. + + methods set_carrid + importing i_carrid type sflight-carrid. + + private section. + class-data go_instance type ref to lcl_context. " Some settings for the production code + +endclass. + +class lcl_context implementation. + method get_instance. " Get sinleton instance + if go_instance is not bound. + create object go_instance. + endif. + ro_instance = go_instance. + endmethod. + + method set_carrid. " Setup context for production environment + clear: me->a_carrid, me->a_testenv. + me->a_carrid = i_carrid. + if i_carrid = 'ZZZ'. " Special test env airline - non existing ! + me->a_testenv = abap_true. + endif. + endmethod. + +endclass. + +********************************************************************** +* SOME BUSINESS LOGIC CLASS - the object to test +********************************************************************** +class lcl_main_logic definition final create public. + + public section. + methods constructor. + methods get_price + importing + i_connid type sflight-connid + i_date type sflight-fldate + returning value(r_price) type sflight-price + exceptions not_found. + + private section. + data o_context type ref to lcl_context. + +endclass. + +class lcl_main_logic implementation. + method constructor. + o_context = lcl_context=>get_instance( ). " Get context + endmethod. + + method get_price. " Get price of the connection in the context airline + data ls_flight type sflight. + + if o_context->a_testenv = abap_false. " Production env + select single price into corresponding fields of ls_flight + from sflight + where carrid = o_context->a_carrid + and connid = i_connid + and fldate = i_date. + else. " Test env + zcl_mockup_loader_store=>retrieve( + exporting + i_name = 'SFLIGHT' + i_sift = i_connid + importing + e_data = ls_flight + exceptions others = 4 ). + endif. + + if sy-subrc is not initial. " Selection error ? + raise not_found. + endif. + + r_price = ls_flight-price. + + endmethod. +endclass. diff --git a/src/zcl_mockup_loader_base_example.clas.testclasses.abap b/src/zcl_mockup_loader_base_example.clas.testclasses.abap new file mode 100644 index 0000000..0548232 --- /dev/null +++ b/src/zcl_mockup_loader_base_example.clas.testclasses.abap @@ -0,0 +1,140 @@ +class ltcl_test_base definition final + for testing + risk level harmless + duration short. + + private section. + + class-methods class_setup. + methods check_ml_version for testing. + +endclass. + +class ltcl_test_base implementation. + + method class_setup. + +* gi_ml->cd( 'testdir' ). " Only if + + endmethod. + + + method check_ml_version. + + data lv_version_ok type abap_bool. + data lv_req_ver type string value zcl_mockup_loader_base_example=>gc_required_mockup_loader_ver. + + lv_version_ok = zcl_mockup_loader=>check_version_fits( lv_req_ver ). + if lv_version_ok = abap_false. + cl_abap_unit_assert=>fail( |mockup loader version ({ zif_mockup_loader=>version }) is lower than required ({ lv_req_ver })| ). + endif. + + endmethod. + +endclass. + +*** + +class ltcl_test definition for testing duration short + risk level harmless. + + public section. + types: + begin of ty_testcase, " test case structure + testid type i, + type type c length 1, + connid type sflight-connid, + result type sflight-price, + msg type string, + end of ty_testcase. + + private section. + data o type ref to lcl_main_logic. " Class being tested + data o_ml type ref to zcl_mockup_loader. " Mockup loader + + methods: setup. + methods: get_price for testing. + +endclass. + +class ltcl_test implementation. + method setup. " Initialize instances + data lo_context type ref to lcl_context. + data lo_ex type ref to cx_static_check. + + lo_context = lcl_context=>get_instance( ). + lo_context->set_carrid( 'ZZZ' ). " Test env airline + + create object o. + + try. + o_ml = zcl_mockup_loader=>create( + i_type = 'MIME' + i_path = 'ZMOCKUP_LOADER_EXAMPLE' + i_encoding = zif_mockup_loader=>encoding_utf16 + i_amt_format = ' ,' ). + catch cx_static_check into lo_ex. + cl_abap_unit_assert=>fail( lo_ex->get_text( ) ). + endtry. + + endmethod. + + method get_price. + data lt_testcases type table of ty_testcase. + data ls_case type ty_testcase. + data lo_ex type ref to cx_static_check. + data l_result type sflight-price. + + try. + " Load test cases index for local usage + o_ml->load_data( + exporting + i_obj = 'EXAMPLE/testcases' + importing + e_container = lt_testcases ). + + " Load and store flights table + zcl_mockup_loader_store=>load_and_store( + io_ml = o_ml + i_obj = 'EXAMPLE/sflight' + i_name = 'SFLIGHT' + i_strict = abap_false + i_tabkey = 'CONNID' + i_type = 'FLIGHTTAB' ). + + catch cx_static_check into lo_ex. + cl_abap_unit_assert=>fail( lo_ex->get_text( ) ). + endtry. + + loop at lt_testcases into ls_case. " Loop through test catalog and run tests + o->get_price( + exporting + i_connid = ls_case-connid + i_date = '20150101' + receiving + r_price = l_result + exceptions + others = 4 ). + + if ls_case-type = '+'. " Positive test + cl_abap_unit_assert=>assert_subrc( + act = sy-subrc + exp = 0 + msg = |[{ ls_case-testid }] { ls_case-msg }| ). + cl_abap_unit_assert=>assert_equals( + act = l_result + exp = ls_case-result + msg = |[{ ls_case-testid }] { ls_case-msg }| ). + + else. "'-' " Negative test + cl_abap_unit_assert=>assert_subrc( + act = sy-subrc + exp = 4 + msg = |[{ ls_case-testid }] { ls_case-msg }| ). + endif. + + endloop. + + endmethod. + +endclass. diff --git a/src/zcl_mockup_loader_base_example.clas.xml b/src/zcl_mockup_loader_base_example.clas.xml new file mode 100644 index 0000000..756bce5 --- /dev/null +++ b/src/zcl_mockup_loader_base_example.clas.xml @@ -0,0 +1,20 @@ + + + + + + ZCL_MOCKUP_LOADER_BASE_EXAMPLE + E + Mockup loader test base class example + 05 + 1 + X + X + X + X + 12 + 11 + + + + diff --git a/src/zmockup_loader_example.prog.abap b/src/zmockup_loader_example.prog.abap deleted file mode 100644 index e0e2475..0000000 --- a/src/zmockup_loader_example.prog.abap +++ /dev/null @@ -1,227 +0,0 @@ -*/--------------------------------------------------------------------------------\ -*| This file is part of Mockup loader | -*| | -*| The MIT License (MIT) | -*| | -*| Copyright (c) 2015 SBCG Team (www.sbcg.com.ua), Alexander Tsybulsky | -*| | -*| Permission is hereby granted, free of charge, to any person obtaining a copy | -*| of this software and associated documentation files (the "Software"), to deal | -*| in the Software without restriction, including without limitation the rights | -*| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | -*| copies of the Software, and to permit persons to whom the Software is | -*| furnished to do so, subject to the following conditions: | -*| | -*| The above copyright notice and this permission notice shall be included in all | -*| copies or substantial portions of the Software. | -*| | -*| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | -*| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | -*| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | -*| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | -*| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | -*| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | -*| SOFTWARE. | -*\--------------------------------------------------------------------------------/ -*/--------------------------------------------------------------------------------\ -*| project homepage: https://github.com/sbcgua/mockup_loader | -*\--------------------------------------------------------------------------------/ - -report zmockup_loader_example. - -********************************************************************** -* SINGLETON SETTINGS CLASS (a sample way to get test env indicator) -********************************************************************** -class lcl_context definition final create private. - - public section. - data a_carrid type sflight-carrid read-only. " Indicates execution in test environment - data a_testenv type abap_bool read-only. - - class-methods get_instance - returning value(ro_instance) type ref to lcl_context. - - methods set_carrid - importing i_carrid type sflight-carrid. - - private section. - class-data go_instance type ref to lcl_context. " Some settings for the production code - -endclass. - -class lcl_context implementation. - method get_instance. " Get sinleton instance - if go_instance is not bound. - create object go_instance. - endif. - ro_instance = go_instance. - endmethod. - - method set_carrid. " Setup context for production environment - clear: me->a_carrid, me->a_testenv. - me->a_carrid = i_carrid. - if i_carrid = 'ZZZ'. " Special test env airline - non existing ! - me->a_testenv = abap_true. - endif. - endmethod. - -endclass. - -********************************************************************** -* SOME BUSINESS LOGIC CLASS - the object to test -********************************************************************** -class lcl_main_logic definition final create public. - - public section. - methods constructor. - methods get_price - importing - i_connid type sflight-connid - i_date type sflight-fldate - returning value(r_price) type sflight-price - exceptions not_found. - - private section. - data o_context type ref to lcl_context. - -endclass. - -class lcl_main_logic implementation. - method constructor. - o_context = lcl_context=>get_instance( ). " Get context - endmethod. - - method get_price. " Get price of the connection in the context airline - data ls_flight type sflight. - - if o_context->a_testenv = abap_false. " Production env - select single price into corresponding fields of ls_flight - from sflight - where carrid = o_context->a_carrid - and connid = i_connid - and fldate = i_date. - else. " Test env - zcl_mockup_loader_store=>retrieve( - exporting - i_name = 'SFLIGHT' - i_sift = i_connid - importing - e_data = ls_flight - exceptions others = 4 ). - endif. - - if sy-subrc is not initial. " Selection error ? - raise not_found. - endif. - - r_price = ls_flight-price. - - endmethod. -endclass. - -********************************************************************** -* TEST CLASS -********************************************************************** -class ltcl_test definition for testing duration short - risk level harmless. - - public section. - types: - begin of ty_testcase, " test case structure - testid type i, - type type c length 1, - connid type sflight-connid, - result type sflight-price, - msg type string, - end of ty_testcase. - - private section. - data o type ref to lcl_main_logic. " Class being tested - data o_ml type ref to zcl_mockup_loader. " Mockup loader - - methods: setup. - methods: get_price for testing. - -endclass. - -class ltcl_test implementation. - method setup. " Initialize instances - data lo_context type ref to lcl_context. - data lo_ex type ref to cx_static_check. - - lo_context = lcl_context=>get_instance( ). - lo_context->set_carrid( 'ZZZ' ). " Test env airline - - create object o. - - try. - o_ml = zcl_mockup_loader=>create( - i_type = 'MIME' - i_path = 'ZMOCKUP_LOADER_EXAMPLE' - i_encoding = zif_mockup_loader=>encoding_utf16 - i_amt_format = ' ,' ). - catch cx_static_check into lo_ex. - cl_abap_unit_assert=>fail( lo_ex->get_text( ) ). - endtry. - - endmethod. - - method get_price. - data lt_testcases type table of ty_testcase. - data ls_case type ty_testcase. - data lo_ex type ref to cx_static_check. - data l_result type sflight-price. - - try. - " Load test cases index for local usage - o_ml->load_data( - exporting - i_obj = 'EXAMPLE/testcases' - importing - e_container = lt_testcases ). - - " Load and store flights table - zcl_mockup_loader_store=>load_and_store( - io_ml = o_ml - i_obj = 'EXAMPLE/sflight' - i_name = 'SFLIGHT' - i_strict = abap_false - i_tabkey = 'CONNID' - i_type = 'FLIGHTTAB' ). - - catch cx_static_check into lo_ex. - cl_abap_unit_assert=>fail( lo_ex->get_text( ) ). - endtry. - - loop at lt_testcases into ls_case. " Loop through test catalog and run tests - o->get_price( - exporting - i_connid = ls_case-connid - i_date = '20150101' - receiving - r_price = l_result - exceptions - others = 4 ). - - if ls_case-type = '+'. " Positive test - cl_abap_unit_assert=>assert_subrc( - act = sy-subrc - exp = 0 - msg = |[{ ls_case-testid }] { ls_case-msg }| ). - cl_abap_unit_assert=>assert_equals( - act = l_result - exp = ls_case-result - msg = |[{ ls_case-testid }] { ls_case-msg }| ). - - else. "'-' " Negative test - cl_abap_unit_assert=>assert_subrc( - act = sy-subrc - exp = 4 - msg = |[{ ls_case-testid }] { ls_case-msg }| ). - endif. - - endloop. - - endmethod. - -endclass. diff --git a/src/zmockup_loader_example.prog.xml b/src/zmockup_loader_example.prog.xml deleted file mode 100644 index 0cea87f..0000000 --- a/src/zmockup_loader_example.prog.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - ZMOCKUP_LOADER_EXAMPLE - 1 - * - T - E - X - X - - - - R - Mockup loader example program - 29 - - - - -