Showing posts with label regular expressions. Show all posts
Showing posts with label regular expressions. Show all posts

ABAP Unit Testing

Unit Testing is one of the corner stones of TDD ( Test Driven Development ). The main goal of  Unit Testing is to test each individual units of code, and determine if it behaves as you designed it to. Each piece of the "puzzle" is tested independently before being integrated into the larger problem you wish to solve. Finding an error in the complete code is much more complex than isolating each unit and then testing it before integrating it into you code.

The biggest resistance to UNIT Testing is the amount of time required to create the tests when you are dealing with a deadline. But there are there are some evidences that TDD and Unit Testing while having an incease of 10-15% in initial time of development, produce a 40-90% decrease in pre-release defects. This are links from Nachiappan Nagapan (link1link2) and another from Boby George and Laurie Williams (link 3)

Unit Tests provide a safety net of regression tests and validation tests so that you can re-factor and integrate effectively. Creating the unit test before the code helps even further by solidifying the requirements, improving developer focus, and avoid creeping elegance.


SAP provides an embedded framework called ABAP UNIT to aid the Test Driven Development approach.


As a basic example we will use our simple Regular Expression email validation program:

http://abapdevblog.blogspot.pt/2013/08/regular-expressions-in-abap-part-1.html


*&---------------------------------------------------------------------*
*& Report  Z_REGEX_EMAIL
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*
REPORT  z_regex_email.

SELECTION-SCREEN BEGIN OF SCREEN 100.
PARAMETERS email TYPE c LENGTH 30 LOWER CASE.
SELECTION-SCREEN END OF SCREEN 100.

CLASS lcl_test_class DEFINITION DEFERRED.

*----------------------------------------------------------------------*
*       CLASS lcl_demo DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_demo DEFINITION
  FRIENDS lcl_test_class. " So that we can test Private & protected methods

  PUBLIC SECTION.
    METHODS: constructor,
            main.
  PRIVATE SECTION.
    METHODS: is_email_valid IMPORTING value(im_email) TYPE char30
                            RETURNING value(re_bool) TYPE abap_bool.

    DATA: mv_email_regex TYPE string.

ENDCLASS.                    "public SECTION.


*----------------------------------------------------------------------*
*       CLASS lcl_demo IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_demo IMPLEMENTATION.
  METHOD constructor.

    " the regular expression to validate the email was retrieved
    " from this wonderful site http://www.regular-expressions.info/
    " which is a great source of knowlege regarding Regular Expression
    mv_email_regex =  '\w+(\.\w+)*@(\w+\.)+((\l|\u){2,4})'.

  ENDMETHOD.                    "class_constructor
  METHOD main.

    CALL SELECTION-SCREEN 100.

    " We use the predifined string function MATCH
    IF abap_true EQ is_email_valid( email ).
      " The Email Matches the Regular expression
      MESSAGE 'Format matches' TYPE 'S'.
    ELSE.
      " There is no match between the email and the regular expression
      MESSAGE 'Format does not Match!' TYPE 'S' DISPLAY LIKE 'E'.
    ENDIF.

  ENDMETHOD.                    "main

  METHOD is_email_valid.
    IF matches( val   = im_email
                regex = mv_email_regex ).
      " The Email Matches the Regular expression
      re_bool = abap_true.
    ELSE.
      re_bool = abap_false.
    ENDIF.
  ENDMETHOD.                    "is_email_valid

ENDCLASS.                    "lcl_demo IMPLEMENTATION



START-OF-SELECTION.
  DATA: lo_demo TYPE REF TO lcl_demo.

  CREATE OBJECT lo_demo.
  lo_demo->main( ).



*----------------------------------------------------------------------*
*       CLASS lcl_Test_Class DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_test_class DEFINITION FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.

  PRIVATE SECTION.
* ================
    DATA:
          f_cut TYPE REF TO lcl_demo.  "class under test


    CLASS-METHODS: class_setup.
    CLASS-METHODS: class_teardown.
    METHODS: setup.
    METHODS: teardown.
    METHODS: email_is_valid FOR TESTING.
    METHODS: email_is_not_valid FOR TESTING.

ENDCLASS.       "lcl_Test_Class
*----------------------------------------------------------------------*
*       CLASS lcl_Test_Class IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_test_class IMPLEMENTATION.
* ====================================

  METHOD class_setup.
* ===================


  ENDMETHOD.       "class_Setup


  METHOD class_teardown.
* ======================


  ENDMETHOD.       "class_Teardown


  METHOD setup.
* =============

    CREATE OBJECT f_cut.
  ENDMETHOD.       "setup


  METHOD teardown.
* ================


  ENDMETHOD.       "teardown

  METHOD email_is_valid.

    cl_abap_unit_assert=>assert_equals(
    EXPORTING
      exp                  = abap_true
      act                  = f_cut->is_email_valid( 'VALID.EMAIL@EMAIL.COM' ) ).

  ENDMETHOD.                    "email_is_valid

  METHOD email_is_not_valid.

    cl_abap_unit_assert=>assert_equals(
    EXPORTING
      exp                  = abap_false
      act                  = f_cut->is_email_valid( 'INVALID@email' ) ).

  ENDMETHOD.                    "email_is_not_valid

ENDCLASS.       "lcl_Test_Class



As you can see we made a couple of changes to the initial program:
  • First the methods from lcl_demo are no longer static methods but instance methods. 
    • This was to show you that the ABAP Unit Framework provides you with some fixture methods to help setup your test data and test object.

  METHOD setup.
* =============
    " There is a SETUP method in a test class, into which you
    " can relocate the data setup that is required before each test.

    CREATE OBJECT f_cut. " we create the instance of the class under test

  ENDMETHOD.       "setup

  • The lcl_demo now has as a friend the new lcl_test_class which has its definition deferred to make the class lcl_test_class known to lcl_demo, regardless of the location of the actual definition of the class in the program. The friendship part is important so that we can grant lcl_test_class access to the protected and private members of lcl_demo class.

CLASS lcl_test_class DEFINITION DEFERRED.

*----------------------------------------------------------------------*
*       CLASS lcl_demo DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_demo DEFINITION
  FRIENDS lcl_test_class. " So that we can test Private & protected method


  • Then we have our new lcl_test_class with a special definition

CLASS lcl_test_class DEFINITION FOR TESTING
                     DURATION SHORT
                     RISK LEVEL HARMLESS.


    • The FOR TESTING addition is used to defined a class as a test class for the ABAP Unit tool.
    • These additions assign test properties to a test class. RISK LEVEL defines the risk level for a test and DURATION the expected execution time. The test properties are checked during when the test is executed. Tests whose risk level is higher than allowed in a system are not executed. Tests that run longer than the expected execution time are terminated.  


    • And our test methods.

    METHODS: email_is_valid FOR TESTING.
    METHODS: email_is_not_valid FOR TESTING.

  METHOD email_is_valid.

    cl_abap_unit_assert=>assert_equals(
    EXPORTING
      exp                  = abap_true
      act                  = f_cut->is_email_valid( 'VALID.EMAIL@EMAIL.COM' ) ).

  ENDMETHOD.                    "email_is_valid

  METHOD email_is_not_valid.

    cl_abap_unit_assert=>assert_equals(
    EXPORTING
      exp                  = abap_false
      act                  = f_cut->is_email_valid( 'INVALID@email' ) ).

  ENDMETHOD.                    "email_is_not_valid


In out initial coding we had a program while re-factoring the is_email_vaild method and introduced a bug. we forgot to change out match variable from email to the importing parameter im_email.


  METHOD is_email_valid.
    IF matches( val   = email
                regex = mv_email_regex ).
      " The Email Matches the Regular expression
      re_bool = abap_true.
    ELSE.
      re_bool = abap_false.
    ENDIF.
  ENDMETHOD.                    "is_email_valid



So when we tested our code  (Ctrl+Shift+F10) we got the following report showing that we have an error.


Execute the Unit Tests



ABAP Unit Report

Being an easy fix we change the code so that we use the import parameter im_email.


  METHOD is_email_valid.
    IF matches( val   = im_email
                regex = mv_email_regex ).
      " The Email Matches the Regular expression
      re_bool = abap_true.
    ELSE.
      re_bool = abap_false.
    ENDIF.
  ENDMETHOD.                    "is_email_valid

Testing again produces the message saying that our method passed all the tests.


Our Unit Tests Passed


Regular Expressions in ABAP ( Part 1 ) - email Validation

Many String operations are difficult to manage one example is the validation of email addresses. this could be easily done with the help of Regular Expressions. 

Invented by american mathematician Stephen Cole Kleenewho helped lay the foundations for theoretical science, regular expressions are a powerful tool for string processing. They are a sequence of characters that form a search pattern, which are used for string or pattern matching within strings.

An email address is generally recognized as having two partes separated by a "At sign" (@). By itself this serves as a basic email address validation. 
If you are interested in a more robust form of validation you can validate the email address according to the RFC 822 grammar (http://www.ietf.org/rfc/rfc0822.txt?number=822), which is a publication of the Internet Engineering Task Force (IETF) and the Internet Society, the principal technical development and standards-setting bodies for the Internet.

Bellow is a simple example of an ABAP email validation program, using the power of Regular Expressions (REGEX).

REPORT  z_regex_email.

SELECTION-SCREEN BEGIN OF SCREEN 100.
PARAMETERS email TYPE c LENGTH 30 LOWER CASE.
SELECTION-SCREEN END OF SCREEN 100.

*----------------------------------------------------------------------*
*       CLASS lcl_demo DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_demo DEFINITION.

  PUBLIC SECTION.
    CLASS-METHODS class_constructor.
    CLASS-METHODS main.
  PRIVATE SECTION.
    CLASS-DATA: mv_email_regex TYPE string.

ENDCLASS.                    "public SECTION.

*----------------------------------------------------------------------*
*       CLASS lcl_demo IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_demo IMPLEMENTATION.
  METHOD class_constructor.

    " Note that this regular expression is by no means bulletproof, as
    " it is extrmely dificult to create such regular expression but it
    " covers most of the cases.
    " 
    mv_email_regex =  '\w+(\.\w+)*@(\w+\.)+((\l|\u){2,4})'.

  ENDMETHOD.                    "class_constructor
  METHOD main.

    CALL SELECTION-SCREEN 100.

    " We use the predifined string function MATCH
    " ( http://help.sap.com/abapdocu_702/en/abenmatch_functions.htm )
    IF matches( val   = email
                regex = mv_email_regex ).
      " The Email Matches the Regular expression
      MESSAGE 'Format matches' TYPE 'S'.
    ELSE.
      " There is no match between the email and the regular expression
      MESSAGE 'Format does not Match!' TYPE 'S' DISPLAY LIKE 'E'.
    ENDIF.

  ENDMETHOD.                    "main

ENDCLASS.                    "lcl_demo IMPLEMENTATION

START-OF-SELECTION.
  lcl_demo=>main( ).