ABAP Unit Test Implementierung

Im vorherigen Beitrag habe ich gezeigt, wie man mit dem Wizard schnell eine Testklasse generieren kann. In diesem Beitrag werde ich die Testklasse vollständig implementieren.

Es wird eine statische Methode getestet. Somit wird keine Instanz der Klasse benötigt. Die Referenzvariable f_Cut und die Instanziierung in der SETUP Methode sind daher überflüssig. Auch die Methoden CLASS_SETUP und CLASS_TEARDOWN werden in diesem Beispiel nicht verwendet. Die beiden Methoden werden in einem anderen Beitrag näher behandelt.

Der Rumpf der Testklasse sieht nun wie folgt aus.

class z_Cl_Date_2_Internal_Unit_Test definition for testing
  duration short
  risk level harmless
.
*?
*?
*?
*?z_Cl_Date_2_Internal_Unit_Test
*?
*?f_Cut
*?
*?Z_CL_CONVERSION_TOOLBOX
*?
*?
*?X
*?
*?X
*?
*?X
*?
*?X
*?
*?
*?
*?
  private section.

    methods: setup.
    methods: teardown.
    methods: date_2_Internal_Format for testing.
endclass.       "z_Cl_Date_2_Internal_Unit_Test


class z_Cl_Date_2_Internal_Unit_Test implementation.

  method setup.
  endmethod.

  method teardown.
  endmethod.

  method date_2_Internal_Format.

    data iv_Input type char10.
    data ev_Output type sydatum.

    z_Cl_Conversion_Toolbox=>date_2_Internal_Format(
      EXPORTING
        IV_INPUT = iv_Input
*     IMPORTING
*       EV_OUTPUT = ev_Output
    ).

    cl_Abap_Unit_Assert=>assert_Equals(
      act   = ev_Output
      exp   = ev_Output          "<--- please adapt expected value
      msg   = 'Testing value ev_Output'
*     level =
    ).
  endmethod.

endclass. 

Durch Versorgen der Variablen IV_INPUT und EV_OUTPUT mit konkreten Werten ist die Testklasse bereits lauffähig. Allerdings wird die Methode dann genau einmal aufgerufen und nur einmal getestet. Ich möchte die Methode allerdings mit vielen Werten aufrufen und prüfen, ob das gewünschte Ergebnis erzielt wurde.

Dazu gibt es grundsätzlich zwei Wege. Entweder man erstellt für jeden einzelnen Testfall genau eine Methode, oder man erstellt eine interne Tabelle mit den Testdaten und verwendet die Tabelle in der Testmethode.

Da der Aufwand für den zweiten Weg deutlich geringer ist, erstelle ich eine interne Tabelle mit Testdaten. Die Testdaten enthalten auch Daten, bei denen die Konvertierung fehlschlagen muss.

Anlegen von Testdaten

Zunächst definiere ich eine interne Tabelle für die Testdaten. Sie enthält die zu testenden formatierten Datumswerte sowie das erwartete Ergebnis als Boolescher Wert. Die Definition wird im Abschnitt PRIVATE SECTION der Testklassendeklaration vorgenommen.

    TYPES: BEGIN OF ty_testdata,
      testdate TYPE char10,
      test_ok TYPE boolean,
      END OF ty_testdata.

    TYPES tyt_testdata TYPE STANDARD TABLE OF ty_testdata WITH DEFAULT KEY. 

    DATA: lt_testdata TYPE tyt_testdata. 

In der Methode SETUP werden nun die Datensätze erstellt. Dabei verwende ich das neue Schlüsselwort VALUE statt per APPEND neue Sätze zu erstellen.

  METHOD setup.

    me->lt_testdata = VALUE tyt_testdata(
     ( testdate = '01.08.2014' result = '20140801' test_ok = abap_true )
     ( testdate = '08/01/2014' result = '20140801' test_ok = abap_true )
     ( testdate = '08-01-2014' result = '20140801' test_ok = abap_true )
     ( testdate = '2014.08.01' result = '20140801' test_ok = abap_true )
     ( testdate = '2014/08/01' result = '20140801' test_ok = abap_true )
     ( testdate = '2014-08-01' result = '20140801' test_ok = abap_true )
     ( testdate = '32.08.2014' result = '20140832' test_ok = abap_false )
     ( testdate = '2014-13-01' result = '20141301' test_ok = abap_false )
     ( testdate = '01:08:2014' result = '20140801' test_ok = abap_false )
    ). 

  ENDMETHOD. 

In der Methode TEARDOWN löschen wir die Testdaten wieder. Dies wäre im konkreten Fall nicht notwendig, da die Daten beim nächsten Testlauf ohnehin neu erstellt werden. Die SETUP Methode wird schließlich vor jeder Testmethode aufgerufen.

  METHOD teardown.

    CLEAR lt_testdata.


  ENDMETHOD. 

Nun kommen wir zur Implementierung der ersten Testmethode. Diese soll alle Datensätze verarbeiten, die erfolgreich sein sollen.

  METHOD date_2_internal_format.

    DATA ev_output TYPE sydatum.

    FIELD-SYMBOLS  TYPE ty_testdata.

    LOOP AT lt_testdata ASSIGNING 
      WHERE test_ok = abap_true.

      z_cl_conversion_toolbox=>date_2_internal_format(
        EXPORTING
          iv_input = -testdate
     IMPORTING
       ev_output = ev_output
      ).

      cl_abap_unit_assert=>assert_equals(
        act   = ev_output
        exp   = -result   
       msg   = 'Datum wurde falsch konvertiert' && | | 
                && -testdate &&
                | | && ev_output && | | &&
                'statt' && | | && -result 
       quit = IF_AUNIT_CONSTANTS=>no
      ).

    ENDLOOP.

  ENDMETHOD. 

Die Methode durchläuft alle Testfälle, die erfolgreich sein sollen. Nach dem Aufruf der Konvertierungsmethode prüft die statische Methode ASSERT_EQUALS, ob das erwartete Ergebnis erzeugt wurde. Wenn nicht, wird eine Nachricht in das Testprotokoll geschrieben. Der Parameter QUIT gibt an, was im Fehlerfall geschehen soll (z.B. Abbruch der Testmethode). In diesem Fall soll nichts weiter passieren.

Nun noch die Methode, welche die falschen Datensätze prüfen soll. Da die statische Methode eine Exception ausgibt, muss der ASSERT anders abgefangen werden.

  METHOD date_2_internal_format_fail.

    DATA ev_output TYPE sydatum.

    FIELD-SYMBOLS  TYPE ty_testdata.

    LOOP AT lt_testdata ASSIGNING 
      WHERE test_ok = abap_false.

      z_cl_conversion_toolbox=>date_2_internal_format(
        EXPORTING
          iv_input = -testdate
     IMPORTING
       ev_output = ev_output
     EXCEPTIONS
       invalid_date = 1
      ).

      cl_abap_unit_assert=>assert_subrc(
        act   = sy-subrc
        exp   = 1
       msg   = 'Datum ist falsch wurde dennoch konvertiert' && | |
                && -testdate && | | 
                && ev_output && | | && 'statt' && | | && -result        
       quit = if_aunit_constants=>no
      ).

    ENDLOOP.

  ENDMETHOD. 

Nun ist die Testklasse vollständig implementiert. Im nächsten Beitrag zeige ich, wie das Testframework aufgerufen wird.