Sei sulla pagina 1di 24

BUnit - BOPF Unit Test

SAP AG, 2016


Disclaimer

This presentation outlines our general product direction and should not be relied on in
making a purchase decision. This presentation is not subject to your license
agreement or any other agreement with SAP. SAP has no obligation to pursue any
course of business outlined in this presentation or to develop or release any
functionality mentioned in this presentation. This presentation and SAP's strategy and
possible future developments are subject to change and may be changed by SAP at
any time for any reason without notice. This document is provided without a warranty
of any kind, either express or implied, including but not limited to, the implied
warranties of merchantability, fitness for a particular purpose, or non-infringement.
SAP assumes no responsibility for errors or omissions in this document, except if
such damages were caused by SAP intentionally or grossly negligent.

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 2


Agenda

Unit Testing
Write Unit Tests with BUnit

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 3


Unit Testing
Unit Testing
Introduction
Test
Method
Service
Manager
BOPF
Runtime

Action Validations

Action InvoicePaid

Determinations

Non-Test Relevant Code


Test Relevant Code

Testing a certain entity (e.g. Action “InvoicePaid”) by calling the Service Manager’s core
service executes much coding that is not test relevant.
Thus the test result does not clearly indicate whether there is an issue in the entity or in its
environment (e.g. other determinations). For instance, a determination may overwrite changes
that are done correctly in the action that is being tested.

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 5


Unit Testing
Definitions

Unit
A unit is the smallest isolatable part of an application that can be tested.
In case of business objects (BOs), a unit is, for instance, an action, a determination
or a validation.

Unit Testing
• Testing each unit separately eases the finding of errors
• By testing only the unit but not its environment, tests are more stable
• Unit testing eases the test-driven development approach

Mock Object / (Test) Double (both terms are used synonymously in the slides)
A mock object is a replacement of a certain object that implements the original
interfaces but returns only test data.

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 6


Unit Testing
Mocking the Internal Access Object (IAO)
Internal
Action Class Access
Test Implementation Object
Method (e.g. InvoicePaid) Double
Export IAO Double
Create IAO Double
(IO_READ, IO_MODIFY)

...
“ retrieve instances IO_READ- >retrieve(…)
Return Test Instances
Return Test
Instances

“ modify instances IO_MODIFY->delete(…)
gv_instance_deleted=X
Return Test
Instances

Check Test Result


(gv_instance_deleted=X)

Usually, an entity implementation accesses the own BO.


For instance, node instances are retrieved. Thus, the internal access objects IAO
casted as IO_READ and IO_MODIFY needs to be mocked to return only test data.
BUnit has mocked most commonly-used methods in /bobf/if_frw_read and
/bobf/if_frw_modify.
© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 7
Write Unit Tests with BUnit
Write Unit Tests with BUnit
Overiew with a Simple Example

Suppose a BO for customer invoice with constants interface


zif_ci_00_customer_invoice_c is available. To write a unit test for actions,
determinations or validations, following operations serve as a minimalist requirement
set for a unit test framework
 Create a root node instance and a child node instance
 Set the attributes of an instance to specific values
 Execute actions, determinations or validations and evaluate the outcomes of them

Using BUnit, those operations are as simple as


DATA(lo_root) = /bobf/cl_bunit=>create_root( zif_ci_00_customer_invoice_c=>sc_bo_key ).
DATA(lo_item) = lo_root->create_child( zif_ci_00_customer_invoice_c=>sc_node-item ).
lo_item->attribute( ‘PRICE’ )->set( 10 ).
lo_item->attribute( ‘QUANTITY’ )->set( 10 ).
lo_item->execute_determination( zif_ci_00_customer_invoice_c=>sc_determination-calc_amount ).
/bobf/cl_bunit=>assert( )->node( lo_item )->attribute( ‘AMOUNT’ )->equals( 100 ).

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 9


Write Unit Tests with BUnit
Create a Node Instance

A root node instance can be created using the static method “create_root” of the class
/bobf/cl_bunit, with the BO key provided
DATA(lo_root) = /bobf/cl_bunit=>create_root( zif_ci_00_customer_invoice_c=>sc_bo_key ).

BO Key

A child node instance can only be created by its parent node instance using the
“create_child” method with the child node key provided
DATA(lo_item) = lo_root->create_child( zif_ci_00_customer_invoice_c=>sc_node-item ).

Parent Node Instance Child Node Key

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 10


Write Unit Tests with BUnit
Set Attributes

To set an attribute of a node instance, the chained call of methods “attribute” and “set”
can be used.
lo_item->attribute( ‘PRICE’ )->set( 10 ).
lo_item->attribute( ‘QUANTITY’ )->set( 10 ).

Name of the Attribute Value of the Attribute

It is also possible to change all attributes of a node instance using the combined
structure with only one function call. Let the combined structure of the root node be of
type “s_customer_invoice”, and there is already a method, say “set_attributes”, to fill
all the fields of such structure. The attributes of a node instance can be set by the
“update” method
DATA ls_root TYPE zci_00_s_root.
set_attributes( IMPORTING is_data = ls_root ).
lo_root->update( is_root ).

Notice the keys (instance key, parent key and root key) in the combined structure
(here is_root) should be consistent with those in the node instance (here lo_root).
Besides, using the “update” method, the values of all attributes in the node instance
will be replaced by those in the combined structure.

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 11


Write Unit Tests with BUnit
Execute Actions and Determinations

Actions and determinations can be executed by calling the methods “execute_action”


and “execute_determination” with the corresponding action key and determination key

DATA(lo_result_det) = lo_item->execute_determination(
zif_ci_00_customer_invoice_c=>sc_determination-item-calculate_amount ).

Result Object (Will Be Discussed Later in Detail) Determination Key

DATA(lo_result_act) = lo_root->execute_action( zif_ci_00_customer_invoice_c=>sc_action-root-invoice_paid ).

Action Key

For simple actions or determinations, where only some attributes get changed, the
assignment DATA(lo_result…) can be omitted, since those attributes can be checked
by asserting those node instances directly. Detailed explanation about result object
and assertion will be discussed in the following slides.

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 12


Write Unit Tests with BUnit
Execute Validations

Consistency validation (check) and action validation (check) need to be called with
different methods, since they have different import parameters.
A consistency validation can be called using the “execute_validation” method with the
validation key
DATA(lo_result_val_c) = lo_item->execute_validation(
zif_ci_00_customer_invoice_c=>sc_validation-item-check_item_currency_code ).

Validation Key

An action validation needs to be called using “execute_action_check” method with the


corresponding action key and the validation key
Action Key
DATA(lo_result_val_a) = lo_root->execute_action_check(
iv_action_key = zif_ci_00_customer_invoice_c=>sc_action-root-invoice_issued
iv_validation_key = zif_ci_00_customer_invoice_c=>sc_validation-root-check_billto_party ).

Validation Key

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 13


Write Unit Tests with BUnit
Assertion and Result Objects – Introduction

The BUnit assert object can be created by the static method “assert” of the class
/bobf/cl_bunit.
DATA(lo_assert) = /bobf/cl_bunit=>assert( ).

BUnit assertion object provides two foundamental ways to check the outcome of an
action, a determination or a validation.
If the attributes of a node are to be checked, the “node” method can be used.
lo_assert->node( lo_item )->equals( lo_item_retrieved_elsewhere ).
lo_assert->node( lo_root )->attribute( ‘TOTAL_AMOUNT’ )->less_or_equal( 100 ). “ String is used for brevity

Node Intances to Be Asserted

If the users want to check whether there are any messages, failed keys or created
instances, the “result” method should be called.
lo_assert->validation_result( lo_result_val )->message(
iv_message_id = zcm_ci_00_messages=>incomplete_bill_to_party-msgid )->is_error( ).
lo_assert->validation_result( lo_result_val )->has_failed_keys( lo_root->get_keys( ) ).
lo_assert->validation_result( lo_result_val )->has_no_created_nodes( ).

Result Object to Be Asserted

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 14


Write Unit Tests with BUnit
Assertion and Result Objects – Assert Node Object

If the BUnit assert object calls the “node” method with a node instance, an assert node
instance is returned. There are several methods implemented by the assert node
object. For detailed information please refer to the
source code. Here, only part of them are explained:
To check whether two node instances share the
same parent instance
lo_assert->node( lo_item_1 )->is_sibling_of( lo_item_2 ).

Assert Node Object

To check whether a node instance has created


any child instances with a specific node key
DATA(lv_node_key) = zif_ci_00_customer_invoice_c=>sc_node-item.
lo_assert->node( lo_root )->has_children( lv_node_key ).

To check whether a node instances has already


been deleted, for example during an action
lo_assert->node( lo_item )->exists( ).

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 15


Write Unit Tests with BUnit
Assertion and Result Objects – Assert Value Object

If only one attribute of a node instance needs to be checked, the assert value object
should be used.
The assert value object is returned by the “attribute” method of the assert node object.
For example, to check whether a root node instance has the expected payment status,
the method “equals” can be used
lo_assert->node( lo_root )->attribute( ‘PAYMENT_STATUS’ )->equals( ‘02’ ).

Assert Value Object

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 16


Write Unit Tests with BUnit
Assertion and Result Objects – Result and Assert Result Objects

As mentioned previously, the methods “execute_action”, “execute_determination”,


“execute_validation” and “execute_action_check” return the BUnit result objects, which
are responsible for those data or objects, which cannot be directly checked by
asserting the node instances. The result objects and assert result objects enable
further checks in the unit test. The assert result objects are obtained by calling the
“action_result” / “determination_result” / “validation_result” methods of the assert
object

lo_assert->validation_esult( lo_result_val_a )->has_no_failed_keys( ).

Validation Result Object

Assert Validation Result Object

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 17


Write Unit Tests with BUnit
Assertion and Result Objects – Result and Assert Result Objects

Due to the differences between actions, determinations and validations, the


corresponding result objects and assert result objects need to be handled differently.

Features / Properties Action Determination Validation


Have import parameter io_modify  
Have export parameter eo_message   
Have export parameter et_failed_key   
Have export parameter et_data 

Action Result Validation Result

Determination Result

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 18


Write Unit Tests with BUnit
Assertion and Result Objects – Result and Assert Result Objects

The following table lists part of the available methods for the three kinds of result
objects
Methods Action Result Determination Result Validation Result

get_failed_keys   

get_messages   

get_created_nodes  

get_exported_data 

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 19


Write Unit Tests with BUnit
Assertion and Result Objects – Result and Assert Result Objects

Result, assert result objects and their usage

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 20


Write Unit Tests with BUnit
Assertion and Result Objects – Assert Message Object

If an operation, say a validation, will generate some messages under certain


circumstances, it is usually expected to assert the details of the messages. The assert
message object is available for such purposes.

lo_assert->validation_result( lo_result_val )->message(


iv_message_id = zcm_ci_00_messages=>incomplete_bill_to_party-msgid
iv_message_no = zcm_ci_00_messages=>incomplete_bill_to_party-msgno )->is_error( ).

Assert Message Object

Notice since both input parameters


(iv_message_id and iv_message_no)
are optional, the method can also be
called without any parameter (meaning
only to check whether there is any
message generated), or only with
iv_message_id (meaning only to check
the expected message ID). Calling the “message” method with iv_message_no but
without iv_message_id makes no sense, thus it is forbidden.

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 21


Write Unit Tests with BUnit
Advanced Topics – Node Set

A node set is one or more node instances with the same node key. A node set can be
obtained by the following ways
DATA(lo_set_1) = /bobf/cl_bunit_node_set=>create_with_node( lo_root_1 ). “ lo_set_1 contains lo_root_1
DATA(lo_set_2) = /bobf/cl_bunit_node_set=>create_empty( ). “ lo_set_2 contains nothing
lo_set_2->add( lo_root_2 ). “ lo_set_2 contains lo_root_2
DATA(lo_set_3) = lo_set_1->merged_with( lo_set_2 ). “ lo_set_3 contains lo_root_1 and lo_root_2

The actions, determinations and validations can be called in exactly the same way as
node object
DATA(lo_result_a) = lo_set_3->execute_action( zif_ci_00_customer_invoice_c=>sc_action-root-invoice_paid ).

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 22


Write Unit Tests with BUnit
Advanced Topics – Created Instances

If an action or a determination create new instances, it should also be possible to


retrieve the created instances and check whether they are created correctly. The
“get_created_nodes” method returns all created node instances with a specified node
ksy as a node set
DATA(lo_result_a) = lo_root->execute_action( zif_ci_00_customer_invoice_c-sc_action-root-create_children ).
DATA(lo_set_item) = lo_result_a->get_created_nodes( zif_ci_00_customer_invoice_c-sc_node-item ).
LOOP AT lo_set_item->get_all( ) INTO DATA(lo_item).
lo_assert->node( lo_item )->attribute( ‘PRICE’ )->greater_than( 1000 ). Specified Node Key

ENDLOOP.

© 2016 SAP SE or an SAP affiliate company. All rights reserved. Internal 23


Thank you

© 2016 SAP SE or an SAP affiliate company. All rights reserved.

Potrebbero piacerti anche