Abap Unit Testing logo

Abap Unit Testing

Community
likweitan
abap-unit-testing

Help with ABAP Unit testing including test class setup, assertions, test doubles, mocking frameworks, dependency injection, CDS test environments, SQL test environments, RAP BO test doubles, and test fixtures. Use when users ask about ABAP unit tests, test classes, test methods, CL_ABAP_UNIT_ASSERT, test doubles, mocking, CDS test environment, SQL test environment, RAP testing, ABAP test injection, test seams, behavior-driven testing, TDD in ABAP, test isolation, or writing automated tests for ABAP code. Triggers include "write a unit test", "create test class", "mock a dependency", "test a CDS view", "test a RAP BO", "test double", "assertion", "test fixture", "test isolation", or "ABAP unit".

Overview

Publisherlikweitan
Repositoryabap-skills
Skill nameabap-unit-testing
Stars
64
Forks
15
Bundled files
1
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 1 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by likweitan on GitHub. Read the source before you install it.

Installation

Install the Abap Unit Testing AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/likweitan/abap-skills.git /tmp/abap-skills
mkdir -p .claude/skills
cp -r /tmp/abap-skills/skills/abap-unit-testing .claude/skills/abap-unit-testing
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Abap Unit Testing in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Abap Unit Testing on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Abap Unit Testing is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

ABAP Unit Testing

Guide for writing effective ABAP Unit tests including test class setup, assertions, test doubles, mocking frameworks, and test environments for CDS, SQL, and RAP.

Workflow

  1. Determine the testing goal:

    • Testing business logic in a class method
    • Testing a CDS view entity
    • Testing a RAP BO behavior implementation
    • Testing database-dependent logic with SQL test doubles
    • Setting up test doubles for external dependencies
  2. Choose the right approach:

    • Direct unit test for pure logic (no dependencies)
    • Constructor/setter injection for mockable dependencies
    • CDS test environment for CDS view tests
    • OSQL test environment for SQL-dependent code
    • RAP BO test doubles for RAP behavior tests
  3. Follow the AAA pattern: Arrange → Act → Assert

  4. Ensure test isolation: Tests must not depend on persistent data or external systems

Test Class Fundamentals

Test Class Definition

abap
"! Test class for ZCL_MY_CLASS
CLASS ltc_my_class DEFINITION FINAL FOR TESTING
  DURATION SHORT
  RISK LEVEL HARMLESS.

  PRIVATE SECTION.
    DATA cut TYPE REF TO zcl_my_class.  "Class Under Test

    CLASS-METHODS class_setup.    "Once before all tests
    CLASS-METHODS class_teardown. "Once after all tests
    METHODS setup.               "Before each test
    METHODS teardown.            "After each test

    METHODS test_calculate_total FOR TESTING.
    METHODS test_validate_input  FOR TESTING.
    METHODS test_empty_input     FOR TESTING RAISING cx_static_check.
ENDCLASS.

CLASS ltc_my_class IMPLEMENTATION.

  METHOD class_setup.
    " One-time setup for all tests in this class
  ENDMETHOD.

  METHOD class_teardown.
    " One-time cleanup
  ENDMETHOD.

  METHOD setup.
    " Create fresh instance before each test
    cut = NEW #( ).
  ENDMETHOD.

  METHOD teardown.
    " Cleanup after each test
  ENDMETHOD.

  METHOD test_calculate_total.
    " Arrange
    DATA(lv_quantity) = 5.
    DATA(lv_price) = CONV decfloat34( '10.50' ).

    " Act
    DATA(lv_result) = cut->calculate_total(
      iv_quantity = lv_quantity
      iv_price    = lv_price ).

    " Assert
    cl_abap_unit_assert=>assert_equals(
      act = lv_result
      exp = CONV decfloat34( '52.50' )
      msg = 'Total should be quantity * price' ).
  ENDMETHOD.

  METHOD test_validate_input.
    cl_abap_unit_assert=>assert_true(
      act = cut->validate_input( 'VALID_INPUT' )
      msg = 'Valid input should return true' ).
  ENDMETHOD.

  METHOD test_empty_input.
    TRY.
        cut->validate_input( '' ).
        cl_abap_unit_assert=>fail( msg = 'Should have raised exception' ).
      CATCH zcx_validation_error INTO DATA(lx_error).
        cl_abap_unit_assert=>assert_bound(
          act = lx_error
          msg = 'Exception should be raised for empty input' ).
    ENDTRY.
  ENDMETHOD.

ENDCLASS.

Test Class Attributes

AttributeOptionsPurpose
DURATIONSHORT / MEDIUM / LONGExpected execution time; SHORT < 1s (default for CI)
RISK LEVELHARMLESS / DANGEROUS / CRITICALImpact on system data; HARMLESS = no DB changes

Test Method Additions

AdditionPurpose
FOR TESTINGMarks method as a test method
RAISING cx_static_checkAllows exceptions to propagate (test fails on unhandled exception)

CL_ABAP_UNIT_ASSERT — Assertion Methods

MethodPurposeExample
assert_equalsValue equalityassert_equals( act = result exp = 42 )
assert_trueBoolean trueassert_true( act = lv_flag )
assert_falseBoolean falseassert_false( act = lv_flag )
assert_initialValue is initialassert_initial( act = lt_table )
assert_not_initialValue is not initialassert_not_initial( act = lt_result )
assert_boundReference is boundassert_bound( act = lo_instance )
assert_not_boundReference is not boundassert_not_bound( act = lo_ref )
assert_differsValues are differentassert_differs( act = val1 exp = val2 )
assert_char_cpCharacter pattern matchassert_char_cp( act = lv_text exp = '*error*' )
assert_char_npCharacter pattern no matchassert_char_np( act = lv_text exp = '*secret*' )
assert_number_betweenNumber in rangeassert_number_between( number = val lower = 1 upper = 10 )
assert_table_containsTable contains lineassert_table_contains( line = wa table = lt_result )
assert_table_not_containsTable does not contain lineassert_table_not_contains( line = wa table = lt_result )
assert_return_codesy-subrc checkassert_return_code( act = sy-subrc exp = 0 )
failForce test failurefail( msg = 'Should not reach here' )

Common Assertion Patterns

abap
" Check table has expected number of entries
cl_abap_unit_assert=>assert_equals(
  act = lines( lt_result )
  exp = 3
  msg = 'Expected 3 result entries' ).

" Check exception message
TRY.
    cut->some_method( ).
    cl_abap_unit_assert=>fail( msg = 'Expected exception' ).
  CATCH zcx_my_exception INTO DATA(lx).
    cl_abap_unit_assert=>assert_equals(
      act = lx->get_text( )
      exp = 'Expected error message' ).
ENDTRY.

" Check that table contains a specific key
cl_abap_unit_assert=>assert_table_contains(
  line  = VALUE zstructure( key_field = 'ABC' )
  table = lt_result
  msg   = 'Result should contain entry ABC' ).

Dependency Injection & Test Doubles

Constructor Injection Pattern

abap
" Production interface
INTERFACE zif_data_provider.
  METHODS get_data
    RETURNING VALUE(rt_data) TYPE ztab_data.
ENDINTERFACE.

" Production class with injectable dependency
CLASS zcl_processor DEFINITION.
  PUBLIC SECTION.
    METHODS constructor
      IMPORTING io_provider TYPE REF TO zif_data_provider OPTIONAL.
    METHODS process
      RETURNING VALUE(rv_result) TYPE string.
  PRIVATE SECTION.
    DATA mo_provider TYPE REF TO zif_data_provider.
ENDCLASS.

CLASS zcl_processor IMPLEMENTATION.
  METHOD constructor.
    mo_provider = COND #(
      WHEN io_provider IS BOUND THEN io_provider
      ELSE NEW zcl_default_provider( ) ).
  ENDMETHOD.

  METHOD process.
    DATA(lt_data) = mo_provider->get_data( ).
    " Process data...
  ENDMETHOD.
ENDCLASS.

Test Double (Manual Mock)

abap
" Test double implementing the interface
CLASS ltd_data_provider DEFINITION FOR TESTING.
  PUBLIC SECTION.
    INTERFACES zif_data_provider.
    DATA mt_test_data TYPE ztab_data.
ENDCLASS.

CLASS ltd_data_provider IMPLEMENTATION.
  METHOD zif_data_provider~get_data.
    rt_data = mt_test_data.
  ENDMETHOD.
ENDCLASS.

" Test class using the double
CLASS ltc_processor DEFINITION FINAL FOR TESTING
  DURATION SHORT RISK LEVEL HARMLESS.
  PRIVATE SECTION.
    DATA cut        TYPE REF TO zcl_processor.
    DATA mo_provider TYPE REF TO ltd_data_provider.

    METHODS setup.
    METHODS test_process_with_data FOR TESTING.
ENDCLASS.

CLASS ltc_processor IMPLEMENTATION.
  METHOD setup.
    mo_provider = NEW #( ).
    cut = NEW #( io_provider = mo_provider ).
  ENDMETHOD.

  METHOD test_process_with_data.
    " Arrange — configure test double
    mo_provider->mt_test_data = VALUE #(
      ( key = '1' value = 'A' )
      ( key = '2' value = 'B' ) ).

    " Act
    DATA(lv_result) = cut->process( ).

    " Assert
    cl_abap_unit_assert=>assert_not_initial( act = lv_result ).
  ENDMETHOD.
ENDCLASS.

CDS Test Environment

Use cl_cds_test_environment=>create( i_for_entity = 'ZI_ENTITY' ) to stub all data sources of a CDS view. Insert test data with insert_test_data( ), clear with clear_doubles( ) in setup, and call destroy( ) in class_teardown.

OSQL (Open SQL) Test Environment

Use cl_osql_test_environment=>create( i_dependency_list = ... ) to stub specific DB tables/views for testing SQL-dependent classes. Same lifecycle pattern as CDS test environment.

RAP BO Test Doubles

  • Transactional buffer double (cl_botd_txbufdbl_bo_test_env): For testing code that consumes a RAP BO via EML
  • Mock EML API (cl_botd_mockemlapi_bo_test_env): For testing RAP handler method implementations by configuring mock responses

Test Seams (Legacy Code)

Use TEST-SEAM / END-TEST-SEAM in production code and TEST-INJECTION / END-TEST-INJECTION in tests. Prefer constructor injection for new code.

For full code examples of all the above, read references/test-environment-examples.md.

Best Practices

Test Design

  • One assertion concept per test — each test should verify one behavior
  • Descriptive test method namestest_reject_negative_quantity not test_1
  • Independent tests — no test should depend on another test's outcome or execution order
  • Fast tests — use DURATION SHORT and avoid unnecessary setup

Test Isolation

  • Always use test doubles for external dependencies (DB, APIs, other BOs)
  • Use setup / teardown to ensure clean state
  • Use class_setup / class_teardown for expensive one-time setup (test environments)
  • Always call clear_doubles( ) in setup for test environment classes
  • Always call destroy( ) in class_teardown

Test Structure

  • Place test classes in local test include (test classes tab in ADT)
  • Prefix test doubles with ltd_ (local test double)
  • Prefix test classes with ltc_ (local test class)
  • Group related tests in the same test class

What to Test

  • Business logic and calculations
  • Validation rules and error cases
  • Edge cases (empty input, boundary values, null references)
  • CDS view calculations and aggregations
  • RAP handler logic (actions, validations, determinations)

What Not to Test

  • Framework-provided functionality (managed CRUD in RAP)
  • Simple getter/setter methods
  • ABAP runtime behavior

References

Related Skills

  • cds-view-entities: Use for testing CDS view entities with CDS test environments
  • rap: Use for testing RAP business objects with RAP test doubles
  • clean-abap: Use for ensuring test code follows Clean ABAP principles

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Abap Unit Testing AI skill do?

Help with ABAP Unit testing including test class setup, assertions, test doubles, mocking frameworks, dependency injection, CDS test environments, SQL test environments, RAP BO test doubles, and test fixtures. Use when users ask about ABAP unit tests, test classes, test methods, CL_ABAP_UNIT_ASSERT, test doubles, mocking, CDS test environment, SQL test environment, RAP testing, ABAP test injection, test seams, behavior-driven testing, TDD in ABAP, test isolation, or writing automated tests for ABAP code. Triggers include "write a unit test", "create test class", "mock a dependency", "test a...

Why use Abap Unit Testing on TypingMind?

Because you install it once and use it with any model. Abap Unit Testing is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Abap Unit Testing in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/likweitan/abap-skills/tree/main/skills/abap-unit-testing. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Abap Unit Testing?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Abap Unit Testing?

As many as you like. As long as a model supports skills, you can use Abap Unit Testing with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Abap Unit Testing AI skill free?

Yes. It is published on GitHub by likweitan under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇