Skip to content

zmsautomation Documentation ​

ZMS Automation - ATAF Integration ​

This module contains API and UI tests for ZMS using ATAF (Test Automation Framework) with Cucumber. zmsautomation is built on it-at-m/agile-test-automation-framework. It uses ATAF without Jira (features in Git) and local Keycloak for SSO — see Local Keycloak setup and the ATAF guide Standalone Usage (No Jira, Local Keycloak).

Prerequisites ​

  • Java 21
  • Maven 3.9+
  • MySQL/MariaDB database (for local testing)
  • Access to Maven Central (ATAF artifacts are published under de.muenchen.ataf:core|rest|web)

Project Structure ​

  • src/test/java/zms/api/ - original REST-assured + JUnit tests (standalone profile)
  • src/test/java/zms/ataf/
    • zms/ataf/rest/steps/ - REST step definitions (REST Assured)
    • zms/ataf/ui/steps/ - UI step definitions (Selenium/ATAF web)
    • zms/ataf/ui/pages/** - page objects for Admin, Statistik, zmscitizenview, Mailinator
  • src/test/resources/features/ - Cucumber feature files
    • rest/zmsapi/ - ZMS REST API features (legacy folder/tag name; targets zmsbackend at /terminvereinbarung/api/2)
    • rest/zmscitizenapi/ - Citizen REST API features
    • ui/zmsadmin/ - Admin UI features
    • ui/zmsstatistic/ - Statistik UI features
    • ui/zmscitizenview/ - CitizenView UI (Service Finder + full booking E2E)
  • src/main/resources/db/migration/ - Flyway database migrations

Running Tests ​

The zmsautomation-test script handles database setup, migrations, and test execution. Scenarios inside one Maven run execute together: 32 at a time for -Pataf-api, 16 browsers at a time for -Pataf-ui. Pass -Ddataproviderthreadcount=1 to run one scenario at a time. How that parallelism was introduced, including the login pools and the races it exposed, is in How We Parallelized zmsautomation.

Each log line starts with the worker thread in brackets, for example [29]. TestNG reuses that thread for the next scenario, so lines with the same number belong to one scenario until that thread logs Starting scenario. Scroll up to the latest Starting scenario line with that number to see which test those lines belong to.

bash
# Run all [ATAF](https://it-at-m.github.io/agile-test-automation-framework/) tests (API + UI)
./zmsautomation/zmsautomation-test -Pataf-api -Pataf-ui

# Run specific tags (scenarios tagged @ignore are excluded unless you include @ignore in the expression)
./zmsautomation/zmsautomation-test -Dcucumber.filter.tags="@smoke"
# Run including ignored scenarios, for example:
# ./zmsautomation/zmsautomation-test -Dcucumber.filter.tags="@ignore and @web"

# Run specific API feature (path is relative to the zmsautomation module)
./zmsautomation/zmsautomation-test -Pataf-api -Dcucumber.features="src/test/resources/features/rest/zmsapi/status.feature"

# Run only API tests (no Selenium)
./zmsautomation/zmsautomation-test -Pataf-api

# Run only UI tests (Selenium/[ATAF](https://it-at-m.github.io/agile-test-automation-framework/) web)
./zmsautomation/zmsautomation-test -Pataf-ui

# Optional: pin a different ATAF Maven version (default: ataf.version in pom.xml)
./zmsautomation/zmsautomation-test -Pataf-api -Pataf-ui -Dataf.version=0.3.3

# One scenario at a time (default is 32 API threads or 16 UI browsers)
./zmsautomation/zmsautomation-test -Pataf-ui -Ddataproviderthreadcount=1

The ATAF library version defaults to <ataf.version> in zmsautomation/pom.xml. Override it with -Dataf.version=… when you need another published de.muenchen.ataf release.

The script will:

  1. Back up the database
  2. Clear caches
  3. Reset database (drop tables)
  4. Import base database (.resources/zms.sql)
  5. Run Flyway migrations (Maven plugin)
  6. Run PHP migrations (zmsbackend migrate)
  7. Run hourly cronjob (with retries)
  8. Run minutely cronjob and slot calculation (calculateSlots)
  9. Perform HTTP health checks (zmsbackend, citizen API, CitizenView, optional refarch-gateway)
  10. Set up display/browser tooling (Xvfb, driver checks)
  11. Run mvn test with your arguments (default tag filter adds not @ignore unless you include @ignore)
  12. Print test reports
  13. Clear caches again
  14. Restore database and Keycloak JSON backups (unless SKIP_DB_RESTORE=1)
  15. Final cleanup (data dir, Flyway test rows, etc.; also registered on EXIT)

Standalone Profile (legacy REST-assured tests) ​

Runs the original JUnit-based REST-assured tests:

bash
cd zmsautomation
mvn test -Pstandalone

ATAF as default ​

The ataf Maven profile is active by default in this module, so mvn test and mvn test-compile already include ATAF/Cucumber/TestNG/Selenium dependencies.

ATAF Profiles (local development) ​

  • Run all ATAF tests (API + UI):
bash
cd zmsautomation
mvn test
  • API-only tests (REST Assured, no Selenium):
bash
mvn test -Pataf-api
# optionally filter:
# mvn test -Pataf-api -Dcucumber.filter.tags="@rest"
# mvn test -Pataf-api -Dcucumber.filter.tags="@zmsapi"
# mvn test -Pataf-api -Dcucumber.filter.tags="@zmscitizenapi"
  • UI-only tests (Selenium/ATAF web, no REST Assured):
bash
mvn test -Pataf-ui
# optionally filter:
# mvn test -Pataf-ui -Dcucumber.filter.tags="@web"
# mvn test -Pataf-ui -Dcucumber.filter.tags="@zmsadmin"
# mvn test -Pataf-ui -Dcucumber.filter.tags="@zmsstatistic"
# mvn test -Pataf-ui -Dcucumber.filter.tags="@zmscitizenview"

macOS host (CLI) ​

From the repo root:

bash
./cli tests install-mac-deps

For Safari also enable: Safari -> Develop -> Allow Remote Automation.

bash
./cli tests run-mac-local --db-full-setup
./cli tests run-mac-local
./cli tests trust-local-gateway

Use --browser safari (or chrome, firefox, edge, safari) as needed.

Environment Variables ​

API Endpoints ​

  • BASE_URI - ZMS API base
  • CITIZEN_API_BASE_URI - Citizen API base (REST steps call zms-web directly)
  • ADMIN_BASE_URI / STATISTIC_BASE_URI - admin/statistic bases
  • CITIZEN_VIEW_BASE_URI / CITIZENVIEW_PORT - CitizenView/Vite server (default 8082)
  • REFARCH_GATEWAY_OFFICES_URL - optional gateway health ping URL
  • SKIP_REFARCH_GATEWAY_HEALTH=1 - skip gateway ping

Database Configuration ​

  • MYSQL_HOST (default db)
  • MYSQL_PORT (default tcp://db:3306)
  • MYSQL_DATABASE (default db)
  • MYSQL_USER (default db)
  • MYSQL_PASSWORD (default db)
  • MYSQL_ROOT_PASSWORD (default root)

UI tests (SSO) ​

For local UI tests (Statistik, Admin), the default SSO user is Keycloak ataf (password vorschau) from Keycloak migration data.

Local Keycloak setup ​

zmsautomation uses ATAF without Jira: all Cucumber features live under src/test/resources/features/, and UI tests authenticate against a local Keycloak — the same keycloakmigration pattern as the RefArch stack. You do not need corporate or government SSO (ssodev) for local runs.

See also the ATAF handbook: Standalone Usage (No Jira, Local Keycloak).

Compose services ​

Keycloak and the migration sidecar are defined in .devcontainer/docker-compose.yaml.

The stack runs:

ServiceRole
keycloakquay.io/keycloak/keycloak:26.6.3, start-dev, KC_HTTP_RELATIVE_PATH=/auth, port 8080
init-keycloakklg71/keycloakmigration:0.2.129, applies migrations once Keycloak is up

init-keycloak mounts .resources/keycloak/migration/ and reads KEYCLOAK_CHANGELOG=/migration/keycloak-changelog.yml.

Migration changelog ​

The changelog applies realm configuration in order:

yaml
includes:
  - path: 01_init-realm.yml # realm zms
  - path: 02_add-clients.yml # OIDC client zms, redirect URIs for admin/statistic
  - path: 03_add-roles.yml
  - path: 04_add-users.yml
  - path: 05_assign-roles.yml
  - path: 06_zms-audience.yml
  - path: 07_add-system-users.yml # test user ataf (password vorschau)
  - path: 08_add-role-test-users.yml

This mirrors the RefArch approach (stack/docker-compose.yml) but uses a ZMS-specific realm, clients, and users.

Hostname keycloak ​

Applications and browser redirects expect the hostname keycloak, not localhost. Add 127.0.0.1 keycloak to your hosts file and restart the stack — see Local Keycloak Setup.

ATAF test properties ​

zmsautomation/src/test/resources/testautomation.properties maps ATAF to the migrated user and bypasses the corporate proxy for Docker hostnames:

properties
testautomation.userName=ataf
testautomation.userPassword=vorschau
testautomation.noProxy=keycloak,citizenview,refarch-gateway,localhost,127.0.0.1

Example ​

bash
export BASE_URI="http://web/terminvereinbarung/api/2"
export CITIZEN_API_BASE_URI="http://web/terminvereinbarung/api/citizen"
export MYSQL_HOST="db"
export MYSQL_PORT="tcp://db:3306"
export MYSQL_DATABASE="db"
export MYSQL_USER="db"
export MYSQL_PASSWORD="db"

cd zmsautomation && mvn test

Database Setup ​

ATAF tests automatically run Flyway migrations before executing tests. Migrations are under src/main/resources/db/migration/.

Some Test Tag Examples ​

  • API tags:
    • @rest
    • @zmsapi (legacy tag; REST API served by zmsbackend)
    • @zmscitizenapi
  • UI tags:
    • @web
    • @zmsadmin
    • @zmsstatistic
    • @zmscitizenview
    • @jumpin
    • @ruppertstrasse
    • @passkalender
    • @hauptkalender
    • @abholung
    • @executeLocally
    • @allowDisabledServicesMix
  • Other:
    • @smoke

@executeLocally is a UI-only tag (@web scenarios), not for pure REST scenarios.

Feature Files ​

API Features (src/test/resources/features/rest/) ​

  • rest/zmsapi/status.feature - status endpoint tests (against zmsbackend)
  • rest/zmscitizenapi/zmskvr-1124_booking_ruppertstrasse_pass_calendar_jumpin_links_citizenapi.feature - Citizen API booking flow

UI Features (src/test/resources/features/ui/) ​

  • ui/zmsadmin/ - Admin UI features
  • ui/zmscitizenview/ - CitizenView booking UI features
  • ui/zmsstatistic/ - Statistik UI features

zmsautomation in GitHub Workflows ​

GitHub Actions workflow: .github/workflows/zmsautomation-workflow.yaml.

Manual runs (workflow_dispatch) expose the usual module/browser/tag inputs plus:

InputPurpose
ataf_versionOptional ATAF Maven version (de.muenchen.ataf:core|rest|web). Leave empty to use ataf.version from zmsautomation/pom.xml on the checked-out branch. When set, the job passes -Dataf.version=… into zmsautomation-test.
serial_scenariosDon't run scenarios in parallel. When checked, each job passes -Ddataproviderthreadcount=1. Off by default, including the nightly schedule.

Scheduled nightly runs always use the POM version (no override). The version must exist on Maven Central.

zmsautomation in Safari on macOS outside the container ​

You can already run Safari-based automation outside the container on macOS. This is currently required for Safari because there is no Safari WebDriver runtime for Linux ARM/AMD containers.

We already provide a CLI setup/run flow in cli_test.py:

bash
# install local macOS test dependencies (includes: sudo safaridriver --enable)
./cli tests install-mac-deps

# run local tests on macOS with Safari
./cli tests run-mac-local --browser safari

In Safari, you must also enable:

  • Safari -> Develop -> Allow Remote Automation

Migration Notes ​

  • zmsautomation uses ATAF + Cucumber; CI/workflows may pin environments separately.

Known limitations ​

Booking tests on public holidays ​

By design, slot calculation does not create appointments on public holidays (dates in the feiertage table seeded by migration V11). The test data for opening hours in zmsautomation (seeded relative to the current date in migrations such as V10 and V19) can overlap with a holiday. When that happens, there may be zero bookable slots on the “first available day,” and booking-related scenarios (Citizen API and CitizenView flows) can fail.

This is expected behavior. Action: re-run the pipeline on the next non‑holiday working day (or run locally on a non‑holiday date).

Statistic and opening-hours seeds around midnight ​

zms-db sets TZ=Europe/Berlin, matching zms-web (zmsbase). Flyway CURDATE() / CURTIME() and Java LocalDate via BerlinTime (admin, citizen API, ZMS API, statistic, and Bürgeransicht steps) therefore share the Berlin calendar day, including between Berlin midnight and UTC midnight (00:00–02:00 CEST in summer, 00:00–01:00 CET in winter).

The scheduled GitHub cron (0 1 * * * = 01:00 UTC ≈ 03:00 CEST / 02:00 CET) already starts after that former UTC/Berlin split and is unrelated.

A genuine Berlin midnight still rolls the calendar day; that is expected. Holiday overlap (above) remains a separate limitation.