diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml
deleted file mode 100644
index 275c1c357..000000000
--- a/.github/workflows/phpunit.yml
+++ /dev/null
@@ -1,235 +0,0 @@
-# SPDX-FileCopyrightText: 2020-2024 Nextcloud GmbH and Nextcloud contributors
-# SPDX-License-Identifier: MIT
-name: PHPUnit
-
-on:
-  pull_request:
-  push:
-    branches:
-      - main
-      - stable*
-
-env:
-  APP_NAME: contacts
-
-jobs:
-  php:
-    runs-on: ubuntu-latest
-
-    strategy:
-      # do not stop on another job's failure
-      fail-fast: false
-      matrix:
-        php-versions: ['8.1']
-        databases: ['sqlite']
-        server-versions: ['master', 'stable30']
-
-    name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
-
-    steps:
-      - name: Checkout server
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          repository: nextcloud/server
-          ref: ${{ matrix.server-versions }}
-          submodules: true
-
-      - name: Checkout app
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          path: apps/${{ env.APP_NAME }}
-
-      - name: Set up php ${{ matrix.php-versions }}
-        uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2
-        with:
-          php-version: ${{ matrix.php-versions }}
-          tools: phpunit
-          extensions: mbstring, iconv, fileinfo, intl, sqlite, pdo_sqlite, gd, zip
-
-      - name: Set up PHPUnit
-        working-directory: apps/${{ env.APP_NAME }}
-        run: composer i
-
-      - name: Set up Nextcloud
-        env:
-          DB_PORT: 4444
-        run: |
-          mkdir data
-          ./occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password
-          ./occ app:enable --force ${{ env.APP_NAME }}
-          php -S localhost:8080 &
-
-      - name: PHPUnit & coverage
-        if: ${{ matrix.server-versions == 'master' }}
-        working-directory: apps/${{ env.APP_NAME }}
-        run: ./vendor/phpunit/phpunit/phpunit --coverage-clover coverage.xml -c phpunit.xml
-
-      - name: PHPUnit
-        if: ${{ matrix.server-versions != 'master' }}
-        working-directory: apps/${{ env.APP_NAME }}
-        run: ./vendor/phpunit/phpunit/phpunit -c phpunit.xml
-
-      # - name: PHPUnit integration
-      #   working-directory: apps/${{ env.APP_NAME }}
-      #   run: ./vendor/phpunit/phpunit/phpunit -c phpunit.integration.xml
-
-      - name: Upload coverage
-        working-directory: apps/${{ env.APP_NAME }}
-        env:
-          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
-        run: bash <(curl -s https://codecov.io/bash)
-
-  mysql:
-    runs-on: ubuntu-latest
-
-    strategy:
-      # do not stop on another job's failure
-      fail-fast: false
-      matrix:
-        php-versions: ['8.1', '8.2', '8.3', '8.4']
-        databases: ['mysql']
-        server-versions: ['master', 'stable30']
-
-    name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
-
-    services:
-      mysql:
-        image: mariadb:10.11
-        ports:
-          - 4444:3306/tcp
-        env:
-          MYSQL_ROOT_PASSWORD: rootpassword
-        options: --health-cmd="mysqladmin ping" --health-interval 5s --health-timeout 2s --health-retries 5
-
-    steps:
-      - name: Checkout server
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          repository: nextcloud/server
-          ref: ${{ matrix.server-versions }}
-          submodules: true
-
-      - name: Checkout app
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          path: apps/${{ env.APP_NAME }}
-
-      - name: Patch for nightly PHP
-        if: ${{ matrix.php-versions == '8.4' }}
-        run: |
-          echo "<?php" > lib/versioncheck.php
-          sed -i 's/max-version="8.1"/max-version="8.2"/' apps/${{ env.APP_NAME }}/appinfo/info.xml
-
-      - name: Set up php ${{ matrix.php-versions }}
-        uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2
-        with:
-          php-version: ${{ matrix.php-versions }}
-          tools: phpunit
-          extensions: mbstring, iconv, fileinfo, intl, mysql, pdo_mysql, gd, zip
-          coverage: none
-
-      - name: Set up PHPUnit
-        working-directory: apps/${{ env.APP_NAME }}
-        run: composer i
-
-      - name: Set up Nextcloud
-        env:
-          DB_PORT: 4444
-        run: |
-          mkdir data
-          ./occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password
-          ./occ app:enable --force ${{ env.APP_NAME }}
-          php -S localhost:8080 &
-
-      - name: PHPUnit
-        working-directory: apps/${{ env.APP_NAME }}
-        run: ./vendor/phpunit/phpunit/phpunit -c phpunit.xml
-
-      # - name: PHPUnit integration
-      #   working-directory: apps/${{ env.APP_NAME }}
-      #   run: ./vendor/phpunit/phpunit/phpunit -c phpunit.integration.xml
-
-  pgsql:
-    runs-on: ubuntu-latest
-
-    strategy:
-      # do not stop on another job's failure
-      fail-fast: false
-      matrix:
-        php-versions: ['8.1']
-        databases: ['pgsql']
-        server-versions: ['master']
-    name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
-
-    services:
-      postgres:
-        image: postgres:17
-        ports:
-          - 4444:5432/tcp
-        env:
-          POSTGRES_USER: root
-          POSTGRES_PASSWORD: rootpassword
-          POSTGRES_DB: nextcloud
-        options: --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5
-
-    steps:
-      - name: Checkout server
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          repository: nextcloud/server
-          ref: ${{ matrix.server-versions }}
-          submodules: true
-
-      - name: Checkout app
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          path: apps/${{ env.APP_NAME }}
-
-      - name: Set up php ${{ matrix.php-versions }}
-        uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2
-        with:
-          php-version: ${{ matrix.php-versions }}
-          tools: phpunit
-          extensions: mbstring, iconv, fileinfo, intl, pgsql, pdo_pgsql, gd, zip
-          coverage: none
-
-      - name: Set up PHPUnit
-        working-directory: apps/${{ env.APP_NAME }}
-        run: composer i
-
-      - name: Set up Nextcloud
-        env:
-          DB_PORT: 4444
-        run: |
-          mkdir data
-          ./occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password
-          ./occ app:enable --force ${{ env.APP_NAME }}
-          php -S localhost:8080 &
-
-      - name: PHPUnit
-        working-directory: apps/${{ env.APP_NAME }}
-        run: ./vendor/phpunit/phpunit/phpunit -c phpunit.xml
-
-      # - name: PHPUnit integration
-      #   working-directory: apps/${{ env.APP_NAME }}
-      #   run: ./vendor/phpunit/phpunit/phpunit -c phpunit.integration.xml
-
-  summary:
-
-    runs-on: ubuntu-latest
-    needs: 
-      - php
-      - mysql
-      - pgsql
-    
-    if: always()
-
-    name: php-test-summary
-
-    steps:
-      - name : Sqlite test status
-        run: if ${{ needs.php.result != 'success' && needs.php.result != 'skipped' }}; then exit 1; fi
-      - name : Mysql test status
-        run: if ${{ needs.mysql.result != 'success' && needs.mysql.result != 'skipped' }}; then exit 1; fi
-      - name : Pgsql test status
-        run: if ${{ needs.pgsql.result != 'success' && needs.pgsql.result != 'skipped' }}; then exit 1; fi    
\ No newline at end of file
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 000000000..bdf7a2516
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,79 @@
+# SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
+# SPDX-License-Identifier: MIT
+name: Test
+on: pull_request
+
+permissions:
+  contents: read
+
+env:
+  APP_NAME: contacts
+
+jobs:
+  backend-unit-tests:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        php-versions: ['8.1', '8.2', '8.3']
+        nextcloud-versions: ['master', 'stable30']
+        include:
+          - php-versions: '8.4'
+            nextcloud-versions: 'master'
+    name: php${{ matrix.php-versions }} branch ${{ matrix.nextcloud-versions }} unit tests
+    
+    steps:
+    - name: Set up Nextcloud env
+      uses: ChristophWurst/setup-nextcloud@fc0790385c175d97e88a7cb0933490de6e990374 # v0.3.2
+      with:
+        nextcloud-version: ${{ matrix.nextcloud-versions }}
+        php-version: ${{ matrix.php-versions }}
+        php-coverage: 'xdebug'
+        patch-php-version-check: ${{ matrix.php-versions == '8.4' }}
+        node-version: 'false'
+        install: true
+    
+    - name: Checkout App
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+      with:
+        path: nextcloud/apps/${{ env.APP_NAME }}
+    
+    - name: Install dependencies
+      working-directory: nextcloud/apps/${{ env.APP_NAME }}
+      run: composer install
+    
+    - name: Run tests
+      working-directory: nextcloud/apps/${{ env.APP_NAME }}
+      run: composer run test:unit
+      if: ${{ matrix.php-versions == '8.3' }}
+      env:
+        XDEBUG_MODE: coverage
+    
+    - name: Run tests
+      working-directory: nextcloud/apps/${{ env.APP_NAME }}
+      run: composer run test:unit
+      if: ${{ matrix.php-versions != '8.3' }}
+      env:
+        XDEBUG_MODE: off
+    
+    - name: Upload coverage to Codecov
+      if: ${{ matrix.php-versions != '8.3' }}
+      uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4
+      with:
+        token: ${{ secrets.CODECOV_TOKEN }}
+        file: nextcloud/apps/${{ env.APP_NAME }}/clover.unit.xml
+        flags: php
+        fail_ci_if_error: ${{ !github.event.pull_request.head.repo.fork }}
+        verbose: true
+
+  summary:
+    runs-on: ubuntu-latest
+    needs:
+      - backend-unit-tests
+
+    if: always()
+
+    name: php-test-summary
+
+    steps:
+      - name: Unit test status
+        run: if ${{ needs.backend-unit-tests.result != 'success' && needs.backend-unit-tests.result != 'skipped' }}; then exit 1; fi
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 000000000..fa635d826
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,6 @@
+# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+# SPDX-License-Identifier: AGPL-3.0-or-later
+comment: false
+ignore:
+  - "tests"
+  - "lib/Vendor"
diff --git a/phpunit.xml b/phpunit.xml
index 199d940cb..b2694452e 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,28 +1,21 @@
-<?xml version="1.0" encoding="utf-8" ?>
+<?xml version="1.0" encoding="utf-8"?>
 <!--
-  - SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
-  - SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
+  - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
   - SPDX-License-Identifier: AGPL-3.0-or-later
 -->
-<phpunit bootstrap="tests/bootstrap.php"
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 bootstrap="tests/bootstrap.php"
 		 verbose="true"
-		 convertDeprecationsToExceptions="true"
-		 colors="true"
-		 timeoutForSmallTests="900"
-		 timeoutForMediumTests="900"
-		 timeoutForLargeTests="900">
-	<testsuite name='Contacts app tests'>
-        <directory>./tests/unit</directory>
+		 xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.5/phpunit.xsd">
+	<testsuite name="unit">
+		<directory suffix="Test.php">./tests/unit</directory>
 	</testsuite>
-	<!-- filters for code coverage -->
-	<filter>
-		<whitelist>
-			<directory suffix=".php">./</directory>
-			<exclude>
-				<directory suffix=".php">./l10n</directory>
-				<directory suffix=".php">./templates</directory>
-				<directory suffix=".php">./tests</directory>
-			</exclude>
-		</whitelist>
-	</filter>
+	<coverage>
+		<include>
+			<directory suffix=".php">./lib</directory>
+		</include>
+		<report>
+			<clover outputFile="./clover.unit.xml"/>
+		</report>
+	</coverage>
 </phpunit>