#24 ABAP Unit for a Screen ​
Testing a screen is usually where enthusiasm goes to die. You either drive a browser, or you mock a framework, or you decide — silently, in a meeting nobody minuted — that this part is tested by the users.
An abap2UI5 app is a global class, and a global class has a Test Classes
include. That is the whole story here, and the reason it stays that short is a
decision in the app rather than in the framework: the logic does not touch
client.
main( ) dispatches — #16. The
methods it dispatches to read data, decide, and change attributes. Only
view_display( ) and the message calls need the client, so a test calls the
other methods directly and looks at the attributes afterwards:
CLASS zcl_app_overdue DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES z2ui5_if_app.
TYPES:
BEGIN OF ty_s_invoice,
invoice TYPE string,
due TYPE d,
open TYPE abap_bool,
END OF ty_s_invoice.
TYPES ty_t_invoice TYPE STANDARD TABLE OF ty_s_invoice WITH EMPTY KEY.
DATA t_invoices TYPE ty_t_invoice.
DATA t_overdue TYPE ty_t_invoice.
DATA key_date TYPE d.
METHODS data_read.
METHODS overdue_calc.
PROTECTED SECTION.
DATA client TYPE REF TO z2ui5_if_client.
METHODS view_display.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_app_overdue IMPLEMENTATION.
METHOD z2ui5_if_app~main.
me->client = client.
CASE abap_true.
WHEN client->check_on_init( ).
key_date = sy-datum.
data_read( ).
overdue_calc( ).
view_display( ).
WHEN client->check_on_navigated( ).
view_display( ).
WHEN client->check_on_event( `RECALC` ).
overdue_calc( ).
ENDCASE.
ENDMETHOD.
METHOD data_read.
" in your system: SELECT FROM the invoice table
t_invoices = VALUE #( ( invoice = `4711` due = `20260801` open = abap_true )
( invoice = `4712` due = `20260930` open = abap_true )
( invoice = `4713` due = `20260701` open = abap_false ) ).
ENDMETHOD.
METHOD overdue_calc.
t_overdue = VALUE #( FOR inv IN t_invoices
WHERE ( open = abap_true AND due < key_date )
( inv ) ).
ENDMETHOD.
METHOD view_display.
DATA(view) = z2ui5_cl_ui5_view_builder=>factory(
)->ele( n = `View` ns = `mvc`
)->a( n = `xmlns` v = `sap.m`
)->a( n = `xmlns:mvc` v = `sap.ui.core.mvc`
)->ele( `Page`
)->a( n = `title` v = `Overdue`
)->tag( `DatePicker`
)->a( n = `value` v = client->_bind( key_date )
)->a( n = `valueFormat` v = `yyyyMMdd`
)->a( n = `displayFormat` v = `medium`
)->a( n = `change` v = client->_event( `RECALC` )
)->ele( `List`
)->a( n = `items` v = client->_bind( t_overdue )
)->ele( `items`
)->tag( `StandardListItem`
)->a( n = `title` v = `{INVOICE}`
)->a( n = `info` v = `{DUE}` ).
client->view_display( view->stringify( ) ).
ENDMETHOD.
ENDCLASS.
The test lives in the include, and the striking thing about it is that nothing in it is abap2UI5:
CLASS ltcl_overdue DEFINITION FINAL
FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
PRIVATE SECTION.
METHODS only_open_and_past_due FOR TESTING.
ENDCLASS.
CLASS ltcl_overdue IMPLEMENTATION.
METHOD only_open_and_past_due.
DATA(cut) = NEW zcl_app_overdue( ).
cut->key_date = `20260901`.
cut->data_read( ).
cut->overdue_calc( ).
cl_abap_unit_assert=>assert_equals(
act = lines( cut->t_overdue )
exp = 1
msg = `4711 is the only open invoice past the key date` ).
ENDMETHOD.
ENDCLASS.
No client, no HTTP, no browser, no test double for a framework. The test sets
attributes, calls a method, reads attributes — the same test it would be for a
class with no screen at all, because up to view_display( ) it is a class
with no screen at all.
Two habits are worth keeping on purpose, and both are free if you start with
them. Handler methods take their input from attributes rather than from
client->get( ), so a test can set the input. And data_read( ) is its own
method, so a test can either run the real SELECT against test data or fill
the table by hand and test the logic alone.
The view itself has a different kind of check. The linter reconstructs the XML the chain builds and holds it against the UI5 metadata — unknown control, misspelled property, wrong type — without a system, in a pipeline, in seconds.
Logic in ABAP Unit, view in the linter, and the roundtrip in a browser. Keep the client out of the logic, and the logic is testable the way any ABAP class is.