Quantcast
Channel: SCN : Blog List - All Communities
Viewing all articles
Browse latest Browse all 2548

How to do convenient multicase unit tests with zmockup_loader

$
0
0

Some time ago I published a post about a mockup loading tool we use for unit testing in our projects http://scn.sap.com/community/abap/blog/2015/11/12/unit-testing-mockup-loader-for-abap. Since then me and my team were using it intensively in our developments and also introduced a couple of new features. This post describes our apprach to unit test preparation.

 

Just briefly for those who didn't read the previous post: the idea of the mockup loader is that you prepare your test data in excel, copy it to a bunch of text files (manually or with excel2txt script that is also availble in repo), zip them, upload as binary object via SMW0, and use mockup loader to extract and deploy this data dynamically for your unit test. For more details see the previous post (link above).

 

Use case

Let's suppose we have a method which is designed to identify quality of some product. We input a table of quality inspection parameters and expect some quality class assessment, in the example "A" or "B". Certain parameters are compulsory and must not be skipped during quality inspection: if they are missing the method raises an exception.

 

The goal: write a unit test code once and then easily update it with new test cases prepared in Excel.

 

Data preparation

Let's say we prepared a unit test table with 3 cases (docno = 10, 20 and 30). Note that QUANTITY parameter is missing in the document 30 - exception expected.

 

DOCNOPARAMVALUE
10WEIGHT12,89
10QUANTITY99
10MOISTURE12,6
20WEIGHT14,89
20QUANTITY103
20MOISTURE11,9
30WEIGHT14,89
30QUANTITY
30MOISTURE12,10

 

Another table is the index of test cases. Each refers to a DOCNO, describes expected result and describes the case in a human-readable form (as a kind of documentation and for failure messages). TYPE field describes if the case is positive or negative.

 

TESTIDTYPEDOCNOEXPECTMSG
1+10ACase with quality A
2+20BCase with quality B
3-30Case with a missing Param

 

 

Unit test code

 

1) Define the test case index structure (in the test class)

 

class lcl_test definition for testing inheriting from cl_aunit_assert

  duration short risk level harmless.

  public section.

    types:

        begin of ty_test_case,

          testid    type i,

          type      type char2,

          docno    type char10,

          expect    type char1,

          msg      type string,

        end of ty_test_case.

 

 

    data at_cases type table of ty_test_case.        " << index table

    data o_ml    type ref to zcl_mockup_loader.    " << mockup loader instance

    data o        type ref to zcl_class_under_test.  " << class under test

    ...

 

2) In setup() method acquire a mockup loader instance and load index table

 

  method setup.

    data lo_ex type ref to zcx_mockup_loader_error.

 

    create object o importing ...    " << Object under test initialization

 

    try.

      o_ml = zcl_mockup_loader=>get_instance( ).

 

      o_ml->load_data(

        exporting i_obj      = 'TEST1/index'    " << file path inside zipfile

        importing e_container = me->at_cases ).

 

    catch cx_static_check into lo_ex.

      fail( lo_ex->get_text( ) ).

    endtry.

  endmethod.

 

3) And here is main test cycle. BTW one of new features of ML introduced recently is data filtering - see the call of o_ml->load_data() - it passes the field name and value. If specified, the mockup loader filters the output to meed this condition. It is possible to define more complex filtering also (more details @github homepage).

 

  method test_assess_quality.

    data l_case  type ty_test_case.

    data lo_ex  type cx_root.

    data lt_insp type zcl_class_under_test=>ty_inspections.

    data l_act  type zcl_class_under_test=>ty_quality.   

 

    loop at at_cases into l_case.                      " << Loop through cases

      clear lo_ex.

 

      try.

        o_ml->load_data(

          exporting i_obj      = 'TEST1/inspections'     

                    i_where    = 'DOCNO = ' && l_case-testid

          importing e_container = lt_insp ). " ^^^ Filter only relevant lines

 

        l_act = o->assess_quality( lt_insp ).                " << Test call

 

      catch zcx_mockup_loader_error into lo_ex.

        fail( lo_ex->get_text( ) ). " Mockup load error handling, just in case

      catch zcx_root into lo_ex.

        " do nothing - this is the expected exception for negative tests

        " better use specific exeption classes of course

      endtry.

 

      if l_case-type = '+'. " Positive test

        " msg indicates the failed test id and it's description

        assert_initial( act = lo_ex

                        msg = |[{ l_case-testid }] { l_case-msg }| ).

        assert_equals( act = l_act

                       exp = l_case-expect

                       msg = |[{ l_case-testid }] { l_case-msg }| ).

      else. " '-'          " Negative test

        assert_not_initial( act = lo_ex

                            msg = |[{ l_case-testid }] { l_case-msg }| ).

        " Potentially more specific error check code should follow

      endif.

 

    endloop.

  endmethod.   

 

4) That's it !

 

Now we've got an easy extendable test infrustructure. New test cases for this test can be added without extra coding. Honestly speaking, this works better for integrated tests than for checking small methods. However, it is very useful when applicable - some methods in out developments have unit tests with 20+ cases and they are still updated from time to time with new ones (usually after yet another bug discovery ).

 

Hope you find it useful !

 

The ABAP mockup loader project lives @github, there you can find full reference of the methods and also some use cases in wiki.

 

All the best, Alexander Tsybulsky.


Viewing all articles
Browse latest Browse all 2548

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>