Samples / Learn / Basics

Basics VI

The app logic in a method of its own - no client, no attributes - so the local test class beside the class can assert it with ABAP Unit.

Class
z2ui5_cl_smp_app_503
Repository
abap2UI5/samples
UI5 libraries
sap.m, sap.ui.core, sap.ui.layout
Lowest UI5 release
1.71
In the browser
Runs in the playground
Controls it builds
sap.m.Button, sap.m.Input, sap.m.Label, sap.m.MessageStrip, sap.m.Page, sap.m.Shell, sap.m.Text, sap.ui.core.mvc.View, sap.ui.layout.form.SimpleForm

The class

" @keywords unit test abapunit testclasses assert testable logic method
" @summary The app logic in a method of its own - no client, no attributes - so the local test class beside the class can assert it with ABAP Unit.
CLASS z2ui5_cl_smp_app_503 DEFINITION PUBLIC.

  PUBLIC SECTION.
    INTERFACES z2ui5_if_app.

    DATA amount TYPE i.
    DATA rate   TYPE i.
    DATA gross  TYPE string.

    "! The logic under test: a net amount plus a percentage, rounded to whole
    "! units. It takes what it needs and returns what it computes - it reads
    "! no attribute, touches no client and starts no roundtrip, which is the
    "! whole reason a test can call it.
    "!
    "! @parameter net | the net amount
    "! @parameter percent | the tax rate, in percent
    "! @parameter result | the gross amount, rounded to whole units, the half
    "!                     away from zero
    "! @raising cx_sy_conversion_overflow | the gross does not fit TYPE i
    METHODS gross_amount
      IMPORTING
        net           TYPE i
        percent       TYPE i
      RETURNING
        VALUE(result) TYPE i
      RAISING
        cx_sy_conversion_overflow.

  PROTECTED SECTION.
    DATA client TYPE REF TO z2ui5_if_client.

    METHODS view_display.

  PRIVATE SECTION.
ENDCLASS.


CLASS z2ui5_cl_smp_app_503 IMPLEMENTATION.


  METHOD z2ui5_if_app~main.

    me->client = client.

    IF client->check_on_init( ).

      amount = 100.
      rate   = 19.

      view_display( ).

    ELSEIF client->check_on_navigated( ).
      view_display( ).

    ELSEIF client->check_on_event( `CALC` ).

      " text typed into an Input bound to TYPE i does not dump: the framework
      " keeps the old number and names the field in t_model_skipped - so the
      " sum would silently use a value the screen no longer shows
      DATA(t_skipped) = client->get( )-t_model_skipped.
      IF t_skipped IS NOT INITIAL.
        gross = ``.
        client->message_box_display( text = |'{ t_skipped[ 1 ]-value }' is no whole number this field can hold - it keeps its old value.|
                                     type = `error` ).
        RETURN.
      ENDIF.

      TRY.
          gross = |{ gross_amount( net = amount percent = rate ) }|.
        CATCH cx_sy_conversion_overflow cx_sy_arithmetic_overflow.
          gross = ``.
          client->message_box_display( text = `The gross amount does not fit an integer - enter a smaller net amount or rate.`
                                       type = `error` ).
      ENDTRY.
    ENDIF.

  ENDMETHOD.


  METHOD gross_amount.

    " computed as a packed number: net * percent leaves the range of TYPE i
    " long before the gross does, and assigning to a packed number with no
    " decimals rounds commercially - the half away from zero, so -10.5 becomes
    " -11 as 10.5 becomes 11. The rounding is the part worth a test.
    DATA gross_p TYPE p LENGTH 16 DECIMALS 0.

    gross_p = net + net * percent / 100.
    result = gross_p.

  ENDMETHOD.


  METHOD view_display.

    DATA(view) = z2ui5_cl_ui5_view_builder=>factory(
        )->ele( n = `View` ns = `mvc`
            )->a( n = `displayBlock` v = `true`
            )->a( n = `height`       v = `100%`
            )->a( n = `xmlns`        v = `sap.m`
            )->a( n = `xmlns:mvc`    v = `sap.ui.core.mvc`
            )->a( n = `xmlns:form`   v = `sap.ui.layout.form` ).

    DATA(page) = view->ele( `Shell`
        )->ele( `Page`
            )->a( n = `title`          v = `abap2UI5 - Basics VI - Unit Tests for the App Logic`
            )->a( n = `showNavButton`  b = client->check_app_prev_stack( )
            )->a( n = `navButtonPress` v = client->_event_nav_app_leave( ) ).

    page->tag( `MessageStrip`
        )->a( n = `text`     v = `The sum below is computed by gross_amount( ), a method that takes what it needs and ` &&
                   `returns what it computes - it reads no attribute and never sees the client. That is what makes ` &&
                   `it testable: the local test class in z2ui5_cl_smp_app_503.clas.testclasses.abap calls it with ` &&
                   `four inputs and asserts the four answers, without a browser, a view or a roundtrip. abapGit ` &&
                   `keeps that file beside the class, and CLSCCINCL in the .clas.xml is what says the class has one.`
        )->a( n = `type`     v = `Information`
        )->a( n = `showIcon` b = abap_true
        )->a( n = `class`    v = `sapUiSmallMargin` ).

    page->ele( n = `SimpleForm` ns = `form`
        )->a( n = `title`    v = `Gross from net`
        )->a( n = `editable` b = abap_true
        )->a( n = `layout`   v = `ResponsiveGridLayout`
        )->ele( n = `content` ns = `form`
            )->tag( `Label`
                )->a( n = `text` v = `Net amount`
            )->tag( `Input`
                )->a( n = `value` v = client->_bind( amount )
            )->tag( `Label`
                )->a( n = `text` v = `Tax rate in percent`
            )->tag( `Input`
                )->a( n = `value` v = client->_bind( rate )
            )->tag( `Label`
                )->a( n = `text` v = `Gross amount`
            )->tag( `Text`
                )->a( n = `text` v = client->_bind( gross )
            )->tag( `Label`
                )->a( n = `text` v = `Run the logic`
            )->tag( `Button`
                )->a( n = `text`  v = `gross_amount( )`
                )->a( n = `type`  v = `Emphasized`
                )->a( n = `press` v = client->_event( `CALC` ) ).

    client->view_display( view->stringify( ) ).

  ENDMETHOD.

ENDCLASS.