diff --git a/Corona-Warn-App/build.gradle b/Corona-Warn-App/build.gradle
index f8ffb33b2ae96d524de4addad7bae3bf4e6d66ec..bd3980e016d94a4a247d65c42f9ab7355bf17d0d 100644
--- a/Corona-Warn-App/build.gradle
+++ b/Corona-Warn-App/build.gradle
@@ -322,6 +322,11 @@ dependencies {
     implementation 'androidx.preference:preference-ktx:1.1.1'
     implementation 'androidx.work:work-runtime-ktx:2.5.0'
 
+    def activity_version = "1.2.3"
+    implementation "androidx.activity:activity-ktx:$activity_version"
+    def fragment_version = "1.3.5"
+    implementation "androidx.fragment:fragment-ktx:$fragment_version"
+
     implementation 'androidx.lifecycle:lifecycle-common-java8:2.3.1'
     implementation 'androidx.lifecycle:lifecycle-process:2.3.1'
     implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
@@ -362,7 +367,8 @@ dependencies {
     testImplementation 'org.hamcrest:hamcrest-library:2.2'
 
     // Testing - jUnit4
-    testImplementation 'junit:junit:4.13.1'
+    def junit_version = "4.13.1"
+    testImplementation "junit:junit:$junit_version"
     testImplementation "org.junit.vintage:junit-vintage-engine:5.7.0"
     testImplementation "androidx.test:core-ktx:1.3.0"
 
@@ -383,7 +389,7 @@ dependencies {
     testImplementation "io.github.classgraph:classgraph:4.8.90"
 
     // Testing - Instrumentation
-    androidTestImplementation 'junit:junit:4.13.1'
+    androidTestImplementation "junit:junit:$junit_version"
     androidTestImplementation 'androidx.test:runner:1.3.0'
     androidTestImplementation 'androidx.test.ext:junit:1.1.2'
     androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/test/ui/CovidCertificateDetailsFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/test/ui/CovidCertificateDetailsFragmentTest.kt
index 124ba8e741f7dd6c8c8adaa600eb8bc0b3cf5617..aab4a8c7249b5998f16f85e1ede7a8f87dfde908 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/test/ui/CovidCertificateDetailsFragmentTest.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/test/ui/CovidCertificateDetailsFragmentTest.kt
@@ -17,9 +17,9 @@ import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePerso
 import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString
 import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate
 import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateIdentifier
-import de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsFragment
-import de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsFragmentArgs
 import de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsViewModel
+import de.rki.coronawarnapp.covidcertificate.test.ui.details.TestCertificateDetailsFragment
+import de.rki.coronawarnapp.covidcertificate.test.ui.details.TestCertificateDetailsFragmentArgs
 import io.mockk.MockKAnnotations
 import io.mockk.every
 import io.mockk.impl.annotations.MockK
@@ -43,7 +43,7 @@ class VaccinationDetailsFragmentTest : BaseUITest() {
     @MockK lateinit var vaccinationDetailsViewModel: CovidCertificateDetailsViewModel
     @MockK lateinit var certificatePersonIdentifier: CertificatePersonIdentifier
 
-    private val args = CovidCertificateDetailsFragmentArgs("testCertificateIdentifier").toBundle()
+    private val args = TestCertificateDetailsFragmentArgs("testCertificateIdentifier").toBundle()
 
     @Before
     fun setUp() {
@@ -61,17 +61,17 @@ class VaccinationDetailsFragmentTest : BaseUITest() {
 
     @Test
     fun launch_fragment() {
-        launchFragment2<CovidCertificateDetailsFragment>(fragmentArgs = args)
+        launchFragment2<TestCertificateDetailsFragment>(fragmentArgs = args)
     }
 
     @Screenshot
     @Test
     fun capture_screenshot_incomplete() {
         every { vaccinationDetailsViewModel.covidCertificate } returns vaccinationDetailsData()
-        launchFragmentInContainer2<CovidCertificateDetailsFragment>(fragmentArgs = args)
-        takeScreenshot<CovidCertificateDetailsFragment>()
+        launchFragmentInContainer2<TestCertificateDetailsFragment>(fragmentArgs = args)
+        takeScreenshot<TestCertificateDetailsFragment>()
         onView(withId(R.id.coordinator_layout)).perform(swipeUp())
-        takeScreenshot<CovidCertificateDetailsFragment>("_2")
+        takeScreenshot<TestCertificateDetailsFragment>("_2")
     }
 
     private fun bitmapLiveDate(): LiveData<Bitmap> {
@@ -143,5 +143,5 @@ class VaccinationDetailsFragmentTest : BaseUITest() {
 @Module
 abstract class CovidCertificateDetailsFragmentTestModule {
     @ContributesAndroidInjector
-    abstract fun covidCertificateDetailsFragment(): CovidCertificateDetailsFragment
+    abstract fun covidCertificateDetailsFragment(): TestCertificateDetailsFragment
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/DigitalCovidCertificateUIModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/DigitalCovidCertificateUIModule.kt
index 8e02465742e84562950ac2af89d0d9fda6845466..fdeb2f4205806ff55f0da7b4fd4fe4808bbc1f89 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/DigitalCovidCertificateUIModule.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/DigitalCovidCertificateUIModule.kt
@@ -1,6 +1,9 @@
 package de.rki.coronawarnapp.covidcertificate
 
 import dagger.Module
+import dagger.android.ContributesAndroidInjector
+import de.rki.coronawarnapp.covidcertificate.common.scan.DccQrCodeScanFragment
+import de.rki.coronawarnapp.covidcertificate.common.scan.DccQrCodeScanModule
 import de.rki.coronawarnapp.covidcertificate.recovery.ui.RecoveryCertificateUIModule
 import de.rki.coronawarnapp.covidcertificate.test.ui.TestCertificateUIModule
 import de.rki.coronawarnapp.covidcertificate.vaccination.ui.VaccinationCertificateUIModule
@@ -12,4 +15,8 @@ import de.rki.coronawarnapp.covidcertificate.vaccination.ui.VaccinationCertifica
         RecoveryCertificateUIModule::class,
     ]
 )
-abstract class DigitalCovidCertificateUIModule
+abstract class DigitalCovidCertificateUIModule {
+
+    @ContributesAndroidInjector(modules = [DccQrCodeScanModule::class])
+    abstract fun dccQrCodeScanFragment(): DccQrCodeScanFragment
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanFragment.kt
similarity index 72%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanFragment.kt
index 5fc1ecbdd0e643141a2baca8be7e111dd8aed511..66a4c1c28f9dc541537c14749c78829a86d63bf8 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanFragment.kt
@@ -1,10 +1,10 @@
-package de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan
+package de.rki.coronawarnapp.covidcertificate.common.scan
 
 import android.Manifest
-import android.content.pm.PackageManager
 import android.os.Bundle
 import android.view.View
 import android.view.accessibility.AccessibilityEvent.TYPE_ANNOUNCEMENT
+import androidx.activity.result.contract.ActivityResultContracts
 import androidx.fragment.app.Fragment
 import com.google.zxing.BarcodeFormat
 import com.journeyapps.barcodescanner.DefaultDecoderFactory
@@ -23,16 +23,32 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
 import de.rki.coronawarnapp.util.viewmodel.cwaViewModels
 import javax.inject.Inject
 
-class VaccinationQrCodeScanFragment :
+class DccQrCodeScanFragment :
     Fragment(R.layout.fragment_scan_qr_code),
     AutoInject {
 
     @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
-    private val viewModel: VaccinationQrCodeScanViewModel by cwaViewModels { viewModelFactory }
+    private val viewModel: DccQrCodeScanViewModel by cwaViewModels { viewModelFactory }
 
     private val binding: FragmentScanQrCodeBinding by viewBinding()
     private var showsPermissionDialog = false
 
+    val requestPermissionLauncher =
+        registerForActivityResult(
+            ActivityResultContracts.RequestPermission()
+        ) { isGranted: Boolean ->
+            if (!isGranted) {
+                if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
+                    showCameraPermissionRationaleDialog()
+                    viewModel.setCameraDeniedPermanently(false)
+                } else {
+                    // User permanently denied access to the camera
+                    showCameraPermissionDeniedDialog()
+                    viewModel.setCameraDeniedPermanently(true)
+                }
+            }
+        }
+
     override fun onViewCreated(
         view: View,
         savedInstanceState: Bundle?
@@ -50,16 +66,27 @@ class VaccinationQrCodeScanFragment :
 
         viewModel.event.observe(viewLifecycleOwner) { event ->
             when (event) {
-                is VaccinationQrCodeScanViewModel.Event.QrCodeScanSucceeded -> {
+                is DccQrCodeScanViewModel.Event.VaccinationQrCodeScanSucceeded -> {
                     binding.qrCodeScanSpinner.hide()
                     doNavigate(
-                        VaccinationQrCodeScanFragmentDirections
-                            .actionVaccinationQrCodeScanFragmentToVaccinationListFragment(
-                                event.personIdentifierCodeSha256
+                        DccQrCodeScanFragmentDirections
+                            .actionDccQrCodeScanFragmentToVaccinationDetailsFragment(
+                                event.certificateId
                             )
                     )
                 }
-                VaccinationQrCodeScanViewModel.Event.QrCodeScanInProgress -> {
+                is DccQrCodeScanViewModel.Event.RecoveryQrCodeScanSucceeded -> { // TODO
+                }
+                is DccQrCodeScanViewModel.Event.TestQrCodeScanSucceeded -> {
+                    binding.qrCodeScanSpinner.hide()
+                    doNavigate(
+                        DccQrCodeScanFragmentDirections
+                            .actionDccQrCodeScanFragmentToTestCertificateDetailsFragment(
+                                event.certificateId
+                            )
+                    )
+                }
+                DccQrCodeScanViewModel.Event.QrCodeScanInProgress -> {
                     binding.qrCodeScanSpinner.show()
                 }
             }
@@ -91,25 +118,6 @@ class VaccinationQrCodeScanFragment :
         requestCameraPermission()
     }
 
-    override fun onRequestPermissionsResult(
-        requestCode: Int,
-        permissions: Array<String>,
-        grantResults: IntArray
-    ) {
-        if (requestCode == REQUEST_CAMERA_PERMISSION_CODE &&
-            grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_DENIED
-        ) {
-            if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
-                showCameraPermissionRationaleDialog()
-                viewModel.setCameraDeniedPermanently(false)
-            } else {
-                // User permanently denied access to the camera
-                showCameraPermissionDeniedDialog()
-                viewModel.setCameraDeniedPermanently(true)
-            }
-        }
-    }
-
     private fun startDecode() = binding.qrCodeScanPreview
         .decodeSingle { barcodeResult ->
             viewModel.onScanResult(barcodeResult)
@@ -151,10 +159,7 @@ class VaccinationQrCodeScanFragment :
         DialogHelper.showDialog(cameraPermissionRationaleDialogInstance)
     }
 
-    private fun requestCameraPermission() = requestPermissions(
-        arrayOf(Manifest.permission.CAMERA),
-        REQUEST_CAMERA_PERMISSION_CODE
-    )
+    private fun requestCameraPermission() = requestPermissionLauncher.launch(Manifest.permission.CAMERA)
 
     private fun leave() {
         showsPermissionDialog = false
@@ -165,8 +170,4 @@ class VaccinationQrCodeScanFragment :
         super.onPause()
         binding.qrCodeScanPreview.pause()
     }
-
-    companion object {
-        private const val REQUEST_CAMERA_PERMISSION_CODE = 4000
-    }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanModule.kt
similarity index 54%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanModule.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanModule.kt
index bc581306a761d7bdfd9e46489b4ca2f98e401dc8..0954b8f29fd35fe26ab23c3c6fd4abba9fc6dd8a 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanModule.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanModule.kt
@@ -1,4 +1,4 @@
-package de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan
+package de.rki.coronawarnapp.covidcertificate.common.scan
 
 import dagger.Binds
 import dagger.Module
@@ -8,11 +8,11 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey
 
 @Module
-abstract class VaccinationQrCodeScanModule {
+abstract class DccQrCodeScanModule {
     @Binds
     @IntoMap
-    @CWAViewModelKey(VaccinationQrCodeScanViewModel::class)
-    abstract fun vaccinationQrCodeScanFragment(
-        factory: VaccinationQrCodeScanViewModel.Factory
+    @CWAViewModelKey(DccQrCodeScanViewModel::class)
+    abstract fun dccQrCodeScanFragment(
+        factory: DccQrCodeScanViewModel.Factory
     ): CWAViewModelFactory<out CWAViewModel>
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..75835156028e515fa19d8021ecdffa1796d82ff5
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanViewModel.kt
@@ -0,0 +1,77 @@
+package de.rki.coronawarnapp.covidcertificate.common.scan
+
+import com.journeyapps.barcodescanner.BarcodeResult
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import de.rki.coronawarnapp.covidcertificate.recovery.core.RecoveryCertificateRepository
+import de.rki.coronawarnapp.covidcertificate.recovery.core.qrcode.RecoveryCertificateQRCode
+import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository
+import de.rki.coronawarnapp.covidcertificate.test.core.qrcode.TestCertificateQRCode
+import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.DccQrCodeValidator
+import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.VaccinationCertificateQRCode
+import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository
+import de.rki.coronawarnapp.util.permission.CameraSettings
+import de.rki.coronawarnapp.util.ui.SingleLiveEvent
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
+import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
+import timber.log.Timber
+
+class DccQrCodeScanViewModel @AssistedInject constructor(
+    private val cameraSettings: CameraSettings,
+    private val qrCodeValidator: DccQrCodeValidator,
+    private val vaccinationRepository: VaccinationRepository,
+    private val testCertificateRepository: TestCertificateRepository,
+    private val recoveryCertificateRepository: RecoveryCertificateRepository
+) : CWAViewModel() {
+
+    val event = SingleLiveEvent<Event>()
+
+    val errorEvent = SingleLiveEvent<Throwable>()
+
+    fun onScanResult(barcodeResult: BarcodeResult) = launch {
+        try {
+            event.postValue(Event.QrCodeScanInProgress)
+            when (val qrCode = qrCodeValidator.validate(barcodeResult.text)) {
+                is VaccinationCertificateQRCode -> registerVaccinationCertificate(qrCode)
+                is TestCertificateQRCode -> registerTestCertificate(qrCode)
+                is RecoveryCertificateQRCode -> registerRecoveryCertificate(qrCode)
+            }
+        } catch (e: Throwable) {
+            errorEvent.postValue(e)
+        }
+    }
+
+    private suspend fun registerVaccinationCertificate(qrCode: VaccinationCertificateQRCode) {
+        val certificate = vaccinationRepository.registerVaccination(qrCode)
+        event.postValue(Event.VaccinationQrCodeScanSucceeded(certificate.certificateId))
+    }
+
+    private suspend fun registerTestCertificate(qrCode: TestCertificateQRCode) {
+        throw NotImplementedError("Test certificate found")
+        // TODO
+//        val certificate = testCertificateRepository.requestCertificate(qrCode)
+//        event.postValue(Event.TestQrCodeScanSucceeded(certificate.certificateId))
+    }
+
+    private suspend fun registerRecoveryCertificate(qrCode: RecoveryCertificateQRCode) {
+        throw NotImplementedError("Recovery certificate found")
+        // TODO
+//        val certificate = recoveryCertificateRepository.requestCertificate(qrCode)
+//        event.postValue(Event.RecoveryQrCodeScanSucceeded(certificate.certificateId))
+    }
+
+    fun setCameraDeniedPermanently(denied: Boolean) {
+        Timber.d("setCameraDeniedPermanently(denied=$denied)")
+        cameraSettings.isCameraDeniedPermanently.update { denied }
+    }
+
+    sealed class Event {
+        object QrCodeScanInProgress : Event()
+        data class VaccinationQrCodeScanSucceeded(val certificateId: String) : Event()
+        data class TestQrCodeScanSucceeded(val certificateId: String) : Event()
+        data class RecoveryQrCodeScanSucceeded(val certificateId: String) : Event()
+    }
+
+    @AssistedFactory
+    interface Factory : SimpleCWAViewModelFactory<DccQrCodeScanViewModel>
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewFragment.kt
index 106e4a95c6da1df1950dc3e07c610244aaf60c6d..475f3e4701ae2805f7a35d99f7580b915caeac48 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewFragment.kt
@@ -2,7 +2,6 @@ package de.rki.coronawarnapp.covidcertificate.person.ui.overview
 
 import android.os.Bundle
 import android.view.View
-import android.widget.Toast
 import androidx.core.view.isGone
 import androidx.core.view.isVisible
 import androidx.fragment.app.Fragment
@@ -14,8 +13,8 @@ import de.rki.coronawarnapp.covidcertificate.common.exception.TestCertificateSer
 import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.CameraPermissionCard
 import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.CertificatesItem
 import de.rki.coronawarnapp.databinding.PersonOverviewFragmentBinding
-import de.rki.coronawarnapp.util.ExternalActionHelper.openUrl
 import de.rki.coronawarnapp.util.ExternalActionHelper.openAppDetailsSettings
+import de.rki.coronawarnapp.util.ExternalActionHelper.openUrl
 import de.rki.coronawarnapp.util.di.AutoInject
 import de.rki.coronawarnapp.util.lists.decorations.TopBottomPaddingDecorator
 import de.rki.coronawarnapp.util.lists.diffutil.update
@@ -81,11 +80,9 @@ class PersonOverviewFragment : Fragment(R.layout.person_overview_fragment), Auto
                 }.show()
             }
 
-            ScanQrCode -> Toast.makeText(
-                requireContext(),
-                "TODO \uD83D\uDEA7 Tomorrow maybe?!",
-                Toast.LENGTH_LONG
-            ).show()
+            ScanQrCode -> doNavigate(
+                PersonOverviewFragmentDirections.actionPersonOverviewFragmentToDccQrCodeScanFragment()
+            )
             OpenAppDeviceSettings -> openAppDetailsSettings()
         }
     }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModel.kt
index b6d64f62878343cdab9128b31a0900db64900dbe..7ed8a797b39145d23bf75f2f81ffb7c924b2dc34 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModel.kt
@@ -139,6 +139,7 @@ class PersonOverviewViewModel @AssistedInject constructor(
 
     private fun Set<PersonCertificates>.filterNotPending() = this
         .filter { !it.hasPendingTestCertificate() }
+        .sortedBy { it.highestPriorityCertificate.fullName }
         .sortedByDescending { it.isCwaUser }
 
     private suspend fun generateQrCode(qrCode: QrCodeString): Bitmap? = try {
@@ -148,7 +149,7 @@ class PersonOverviewViewModel @AssistedInject constructor(
         null
     }
 
-    private fun refreshCertificate(identifier: TestCertificateIdentifier) =
+    fun refreshCertificate(identifier: TestCertificateIdentifier) =
         launch {
             val error = testCertificateRepository.refresh(identifier).mapNotNull { it.error }.singleOrNull()
             error?.let { events.postValue(ShowRefreshErrorDialog(error)) }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/TestCertificateUIModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/TestCertificateUIModule.kt
index 23e9fa4035cc3f7fa118c2e5421ce768e3251e5f..d1d1a28cf47b61e0e321df9c397fae0913b5d9f6 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/TestCertificateUIModule.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/TestCertificateUIModule.kt
@@ -4,8 +4,8 @@ import dagger.Module
 import dagger.android.ContributesAndroidInjector
 import de.rki.coronawarnapp.covidcertificate.person.ui.overview.PersonOverviewFragment
 import de.rki.coronawarnapp.covidcertificate.person.ui.overview.PersonOverviewFragmentModule
-import de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsFragment
 import de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsModule
+import de.rki.coronawarnapp.covidcertificate.test.ui.details.TestCertificateDetailsFragment
 
 @Module
 abstract class TestCertificateUIModule {
@@ -14,7 +14,7 @@ abstract class TestCertificateUIModule {
     abstract fun certificatesFragment(): CertificatesFragment
 
     @ContributesAndroidInjector(modules = [CovidCertificateDetailsModule::class])
-    abstract fun certificateDetailsFragment(): CovidCertificateDetailsFragment
+    abstract fun certificateDetailsFragment(): TestCertificateDetailsFragment
 
     @ContributesAndroidInjector(modules = [PersonOverviewFragmentModule::class])
     abstract fun personOverviewFragment(): PersonOverviewFragment
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/TestCertificateDetailsFragment.kt
similarity index 87%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsFragment.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/TestCertificateDetailsFragment.kt
index c1e6501dab3b07d90de7a771569f387d51d95e89..3b25fb31f99e957f744e2ba8e275fac862311d13 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/TestCertificateDetailsFragment.kt
@@ -13,7 +13,7 @@ import com.google.android.material.appbar.AppBarLayout
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.bugreporting.ui.toErrorDialogBuilder
 import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate
-import de.rki.coronawarnapp.databinding.FragmentCovidCertificateDetailsBinding
+import de.rki.coronawarnapp.databinding.FragmentTestCertificateDetailsBinding
 import de.rki.coronawarnapp.ui.qrcode.fullscreen.QrCodeFullScreenFragmentArgs
 import de.rki.coronawarnapp.ui.view.onOffsetChange
 import de.rki.coronawarnapp.util.DialogHelper
@@ -28,11 +28,11 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
 import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted
 import javax.inject.Inject
 
-class CovidCertificateDetailsFragment : Fragment(R.layout.fragment_covid_certificate_details), AutoInject {
+class TestCertificateDetailsFragment : Fragment(R.layout.fragment_test_certificate_details), AutoInject {
 
     @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
-    private val binding by viewBinding<FragmentCovidCertificateDetailsBinding>()
-    private val args by navArgs<CovidCertificateDetailsFragmentArgs>()
+    private val binding by viewBinding<FragmentTestCertificateDetailsBinding>()
+    private val args by navArgs<TestCertificateDetailsFragmentArgs>()
     private val viewModel: CovidCertificateDetailsViewModel by cwaViewModelsAssisted(
         factoryProducer = { viewModelFactory },
         constructorCall = { factory, _ ->
@@ -58,7 +58,7 @@ class CovidCertificateDetailsFragment : Fragment(R.layout.fragment_covid_certifi
         viewModel.covidCertificate.observe(viewLifecycleOwner) { it?.let { onCertificateReady(it) } }
     }
 
-    private fun FragmentCovidCertificateDetailsBinding.onCertificateReady(
+    private fun FragmentTestCertificateDetailsBinding.onCertificateReady(
         testCertificate: TestCertificate
     ) {
         val testDateTime = testCertificate.sampleCollectedAt.toUserTimeZone()
@@ -80,7 +80,7 @@ class CovidCertificateDetailsFragment : Fragment(R.layout.fragment_covid_certifi
         certificateId.text = testCertificate.certificateId
     }
 
-    private fun FragmentCovidCertificateDetailsBinding.onQrCodeReady(bitmap: Bitmap?) {
+    private fun FragmentTestCertificateDetailsBinding.onQrCodeReady(bitmap: Bitmap?) {
         qrCodeCard.apply {
             image.setImageBitmap(bitmap)
             progressBar.hide()
@@ -88,12 +88,12 @@ class CovidCertificateDetailsFragment : Fragment(R.layout.fragment_covid_certifi
         }
     }
 
-    private fun FragmentCovidCertificateDetailsBinding.onError(error: Throwable) {
+    private fun FragmentTestCertificateDetailsBinding.onError(error: Throwable) {
         qrCodeCard.progressBar.hide()
         error.toErrorDialogBuilder(requireContext()).show()
     }
 
-    private fun FragmentCovidCertificateDetailsBinding.onNavEvent(event: CovidCertificateDetailsNavigation) {
+    private fun FragmentTestCertificateDetailsBinding.onNavEvent(event: CovidCertificateDetailsNavigation) {
         when (event) {
             CovidCertificateDetailsNavigation.Back -> popBackStack()
             is CovidCertificateDetailsNavigation.FullQrCode -> findNavController().navigate(
@@ -105,7 +105,7 @@ class CovidCertificateDetailsFragment : Fragment(R.layout.fragment_covid_certifi
         }
     }
 
-    private fun FragmentCovidCertificateDetailsBinding.bindToolbar() = toolbar.apply {
+    private fun FragmentTestCertificateDetailsBinding.bindToolbar() = toolbar.apply {
         setNavigationOnClickListener { popBackStack() }
         setOnMenuItemClickListener {
             when (it.itemId) {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/VaccinationCertificateUIModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/VaccinationCertificateUIModule.kt
index 961a18479dd77f5d4a91411fd79a69c9f8f79851..f2778682c41b272c1970234dd93bfe259638ed93 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/VaccinationCertificateUIModule.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/VaccinationCertificateUIModule.kt
@@ -8,8 +8,6 @@ import de.rki.coronawarnapp.covidcertificate.vaccination.ui.details.VaccinationD
 import de.rki.coronawarnapp.covidcertificate.vaccination.ui.details.VaccinationDetailsFragmentModule
 import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListFragment
 import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListFragmentModule
-import de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan.VaccinationQrCodeScanFragment
-import de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan.VaccinationQrCodeScanModule
 
 @Module
 abstract class VaccinationCertificateUIModule {
@@ -20,9 +18,6 @@ abstract class VaccinationCertificateUIModule {
     @ContributesAndroidInjector(modules = [VaccinationDetailsFragmentModule::class])
     abstract fun vaccinationDetailsFragment(): VaccinationDetailsFragment
 
-    @ContributesAndroidInjector(modules = [VaccinationQrCodeScanModule::class])
-    abstract fun vaccinationQrCodeScanFragment(): VaccinationQrCodeScanFragment
-
     @ContributesAndroidInjector(modules = [VaccinationConsentFragmentModule::class])
     abstract fun vaccinationConsentFragment(): VaccinationConsentFragment
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragment.kt
index 0960f36b80d642062e182601974c309571e11358..9fbf7a9d937c768f78d67dd5676ceef83c213df4 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragment.kt
@@ -17,8 +17,8 @@ import de.rki.coronawarnapp.bugreporting.ui.toErrorDialogBuilder
 import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson
 import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListViewModel.Event.DeleteVaccinationEvent
 import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListViewModel.Event.NavigateBack
+import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListViewModel.Event.NavigateToQrCodeScanScreen
 import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListViewModel.Event.NavigateToVaccinationCertificateDetails
-import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListViewModel.Event.NavigateToVaccinationQrCodeScanScreen
 import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.VaccinationListAdapter
 import de.rki.coronawarnapp.databinding.FragmentVaccinationListBinding
 import de.rki.coronawarnapp.ui.qrcode.fullscreen.QrCodeFullScreenFragmentArgs
@@ -74,8 +74,8 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au
                         VaccinationListFragmentDirections
                             .actionVaccinationListFragmentToVaccinationDetailsFragment(event.vaccinationCertificateId)
                     )
-                    is NavigateToVaccinationQrCodeScanScreen -> doNavigate(
-                        VaccinationListFragmentDirections.actionVaccinationListFragmentToVaccinationQrCodeScanFragment()
+                    is NavigateToQrCodeScanScreen -> doNavigate(
+                        VaccinationListFragmentDirections.actionVaccinationListFragmentToDccQrCodeScanFragment()
                     )
                     is VaccinationListViewModel.Event.NavigateToQrCodeFullScreen -> {
                         val navigatorExtras =
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListViewModel.kt
index 9dea2c3500fe560abdba6ef75945cd14967b6087..60fec58922449917e8efcc01eaa36ef4778a38eb 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListViewModel.kt
@@ -142,7 +142,7 @@ class VaccinationListViewModel @AssistedInject constructor(
         }.toList()
 
     fun onRegisterNewVaccinationClick() {
-        events.postValue(Event.NavigateToVaccinationQrCodeScanScreen)
+        events.postValue(Event.NavigateToQrCodeScanScreen)
     }
 
     fun deleteVaccination(vaccinationCertificateId: String) {
@@ -163,7 +163,7 @@ class VaccinationListViewModel @AssistedInject constructor(
 
     sealed class Event {
         data class NavigateToVaccinationCertificateDetails(val vaccinationCertificateId: String) : Event()
-        object NavigateToVaccinationQrCodeScanScreen : Event()
+        object NavigateToQrCodeScanScreen : Event()
         data class NavigateToQrCodeFullScreen(val qrCode: String, val positionInList: Int) : Event()
         data class DeleteVaccinationEvent(val vaccinationCertificateId: String, val position: Int? = null) : Event()
         object NavigateBack : Event()
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt
deleted file mode 100644
index 64dfd89b0c4abfb147ee57d8bb2ae511f0f0a58e..0000000000000000000000000000000000000000
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan
-
-import com.journeyapps.barcodescanner.BarcodeResult
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import de.rki.coronawarnapp.covidcertificate.common.exception.InvalidHealthCertificateException.ErrorCode.NO_VACCINATION_ENTRY
-import de.rki.coronawarnapp.covidcertificate.common.exception.InvalidVaccinationCertificateException
-import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.DccQrCodeValidator
-import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.VaccinationCertificateQRCode
-import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository
-import de.rki.coronawarnapp.util.permission.CameraSettings
-import de.rki.coronawarnapp.util.ui.SingleLiveEvent
-import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
-import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
-import timber.log.Timber
-
-class VaccinationQrCodeScanViewModel @AssistedInject constructor(
-    private val cameraSettings: CameraSettings,
-    private val vaccinationQRCodeValidator: DccQrCodeValidator,
-    private val vaccinationRepository: VaccinationRepository
-) : CWAViewModel() {
-
-    val event = SingleLiveEvent<Event>()
-
-    val errorEvent = SingleLiveEvent<Throwable>()
-
-    fun onScanResult(barcodeResult: BarcodeResult) = launch {
-        try {
-            event.postValue(Event.QrCodeScanInProgress)
-            val qrCode = vaccinationQRCodeValidator.validate(barcodeResult.text)
-            if (qrCode !is VaccinationCertificateQRCode) {
-                throw InvalidVaccinationCertificateException(NO_VACCINATION_ENTRY)
-            }
-            val vaccinationCertificate = vaccinationRepository.registerVaccination(qrCode)
-            event.postValue(Event.QrCodeScanSucceeded(vaccinationCertificate.personIdentifier.codeSHA256))
-        } catch (e: Throwable) {
-            errorEvent.postValue(e)
-        }
-    }
-
-    fun setCameraDeniedPermanently(denied: Boolean) {
-        Timber.d("setCameraDeniedPermanently(denied=$denied)")
-        cameraSettings.isCameraDeniedPermanently.update { denied }
-    }
-
-    sealed class Event {
-        object QrCodeScanInProgress : Event()
-        data class QrCodeScanSucceeded(val personIdentifierCodeSha256: String) : Event()
-    }
-
-    @AssistedFactory
-    interface Factory : SimpleCWAViewModelFactory<VaccinationQrCodeScanViewModel>
-}
diff --git a/Corona-Warn-App/src/main/res/layout/fragment_covid_certificate_details.xml b/Corona-Warn-App/src/main/res/layout/fragment_test_certificate_details.xml
similarity index 100%
rename from Corona-Warn-App/src/main/res/layout/fragment_covid_certificate_details.xml
rename to Corona-Warn-App/src/main/res/layout/fragment_test_certificate_details.xml
diff --git a/Corona-Warn-App/src/main/res/navigation/certificate_graph.xml b/Corona-Warn-App/src/main/res/navigation/certificate_graph.xml
index 8e21587d789c80a60f3b3f84e4d139ad168dca59..c9e3e74b14b1e9b519c224430baaaf541f106374 100644
--- a/Corona-Warn-App/src/main/res/navigation/certificate_graph.xml
+++ b/Corona-Warn-App/src/main/res/navigation/certificate_graph.xml
@@ -15,6 +15,9 @@
         <action
             android:id="@+id/action_personOverviewFragment_to_vaccinationListFragment"
             app:destination="@id/vaccinationListFragment" />
+        <action
+            android:id="@+id/action_personOverviewFragment_to_dccQrCodeScanFragment"
+            app:destination="@id/dccQrCodeScanFragment" />
     </fragment>
 
     <fragment
@@ -43,10 +46,10 @@
         tools:layout="@layout/fragment_information_privacy" />
 
     <fragment
-        android:id="@+id/covidCertificateDetailsFragment"
-        android:name="de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsFragment"
+        android:id="@+id/testCertificateDetailsFragment"
+        android:name="de.rki.coronawarnapp.covidcertificate.test.ui.details.TestCertificateDetailsFragment"
         android:label="CovidCertificateDetailsFragment"
-        tools:layout="@layout/fragment_covid_certificate_details">
+        tools:layout="@layout/fragment_test_certificate_details">
 
         <argument
             android:name="testCertificateIdentifier"
@@ -54,16 +57,20 @@
     </fragment>
 
     <fragment
-        android:id="@+id/vaccinationQrCodeScanFragment"
-        android:name="de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan.VaccinationQrCodeScanFragment"
-        android:label="VaccinationQrCodeScanFragment"
+        android:id="@+id/dccQrCodeScanFragment"
+        android:name="de.rki.coronawarnapp.covidcertificate.common.scan.DccQrCodeScanFragment"
+        android:label="DccQrCodeScanFragment"
         tools:layout="@layout/fragment_scan_qr_code">
         <action
-            android:id="@+id/action_vaccinationQrCodeScanFragment_to_vaccinationListFragment"
-            app:destination="@id/vaccinationListFragment"
-            app:launchSingleTop="true"
-            app:popUpTo="@id/vaccinationQrCodeScanFragment"
-            app:popUpToInclusive="true"/>
+            android:id="@+id/action_dccQrCodeScanFragment_to_vaccinationDetailsFragment"
+            app:destination="@id/vaccinationDetailsFragment"
+            app:popUpTo="@id/dccQrCodeScanFragment"
+            app:popUpToInclusive="true" />
+        <action
+            android:id="@+id/action_dccQrCodeScanFragment_to_testCertificateDetailsFragment"
+            app:destination="@id/testCertificateDetailsFragment"
+            app:popUpTo="@id/dccQrCodeScanFragment"
+            app:popUpToInclusive="true" />
     </fragment>
 
     <fragment
@@ -79,11 +86,11 @@
             app:destination="@id/vaccinationDetailsFragment" />
         <deepLink app:uri="coronawarnapp://vaccination-list/{personIdentifierCodeSha256}" />
         <action
-            android:id="@+id/action_vaccinationListFragment_to_vaccinationQrCodeScanFragment"
-            app:destination="@id/vaccinationQrCodeScanFragment" />
+            android:id="@+id/action_vaccinationListFragment_to_dccQrCodeScanFragment"
+            app:destination="@id/dccQrCodeScanFragment" />
         <action
             android:id="@+id/action_vaccinationListFragment_to_covidCertificateDetailsFragment"
-            app:destination="@id/covidCertificateDetailsFragment" />
+            app:destination="@id/testCertificateDetailsFragment" />
     </fragment>
 
     <fragment
@@ -95,4 +102,4 @@
             android:name="vaccinationCertificateId"
             app:argType="string" />
     </fragment>
-</navigation>
\ No newline at end of file
+</navigation>
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonCertificatesData.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonCertificatesData.kt
new file mode 100644
index 0000000000000000000000000000000000000000..49097d8ba0f92992a5c585a1b8ed98056fb94473
--- /dev/null
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonCertificatesData.kt
@@ -0,0 +1,71 @@
+package de.rki.coronawarnapp.covidcertificate.person.ui.overview
+
+import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier
+import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString
+import de.rki.coronawarnapp.covidcertificate.person.core.PersonCertificates
+import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate
+import org.joda.time.Instant
+import org.joda.time.LocalDate
+
+object PersonCertificatesData {
+    val certificatesWithPending = mutableSetOf<PersonCertificates>()
+        .apply {
+            add(PersonCertificates(listOf(testCertificate(fullName = "Andrea Schneider"))))
+            add(PersonCertificates(listOf(testCertificate(fullName = "Max Mustermann", isPending = true))))
+            add(PersonCertificates(listOf(testCertificate(fullName = "Zeebee")), isCwaUser = true))
+        }
+    val certificatesWithUpdating = mutableSetOf<PersonCertificates>().apply {
+        add(PersonCertificates(listOf(testCertificate(fullName = "Andrea Schneider"))))
+        add(PersonCertificates(listOf(testCertificate(fullName = "Zeebee")), isCwaUser = true))
+        add(
+            PersonCertificates(
+                listOf(testCertificate(fullName = "Max Mustermann", isPending = true, isUpdating = true))
+            )
+        )
+    }
+    val certificatesWithCwaUser = mutableSetOf<PersonCertificates>().apply {
+        add(PersonCertificates(listOf(testCertificate(fullName = "Max Mustermann"))))
+        add(PersonCertificates(listOf(testCertificate(fullName = "Erika Musterfrau"))))
+        add(PersonCertificates(listOf(testCertificate(fullName = "Andrea Schneider"))))
+        add(PersonCertificates(listOf(testCertificate(fullName = "Zeebee")), isCwaUser = true))
+        add(PersonCertificates(listOf(testCertificate(fullName = "Zeebee A"))))
+    }
+    val certificatesWithoutCwaUser = mutableSetOf<PersonCertificates>().apply {
+        add(PersonCertificates(listOf(testCertificate("Max Mustermann"))))
+        add(PersonCertificates(listOf(testCertificate("Erika Musterfrau"))))
+        add(PersonCertificates(listOf(testCertificate("Andrea Schneider"))))
+    }
+}
+
+fun testCertificate(
+    fullName: String,
+    isPending: Boolean = false,
+    isUpdating: Boolean = false
+) = object : TestCertificate {
+    override val targetName: String = "targetName"
+    override val testType: String = "testType"
+    override val testResult: String = "testResult"
+    override val testName: String = "testName"
+    override val testNameAndManufacturer: String = "testNameAndManufacturer"
+    override val sampleCollectedAt: Instant = Instant.EPOCH
+    override val testCenter: String = ""
+    override val registeredAt: Instant = Instant.EPOCH
+    override val isUpdatingData: Boolean = isUpdating
+    override val isCertificateRetrievalPending: Boolean = isPending
+    override val issuer: String = "issuer"
+    override val issuedAt: Instant = Instant.EPOCH
+    override val expiresAt: Instant = Instant.EPOCH
+    override val qrCode: QrCodeString = "qrCode"
+    override val firstName: String = "firstName"
+    override val lastName: String = "lastName"
+    override val fullName: String = fullName
+    override val dateOfBirth: LocalDate = LocalDate.now()
+    override val personIdentifier = CertificatePersonIdentifier(
+        dateOfBirth = LocalDate(System.currentTimeMillis()),
+        lastNameStandardized = "lastNameStandardized",
+        firstNameStandardized = "firstNameStandardized"
+    )
+    override val certificateIssuer: String = "certificateIssuer"
+    override val certificateCountry: String = "certificateCountry"
+    override val certificateId: String = "certificateId"
+}
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModelTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5aea178460619087a437010c9aed7daddddf7431
--- /dev/null
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModelTest.kt
@@ -0,0 +1,149 @@
+package de.rki.coronawarnapp.covidcertificate.person.ui.overview
+
+import android.content.Context
+import de.rki.coronawarnapp.contactdiary.util.getLocale
+import de.rki.coronawarnapp.covidcertificate.person.core.PersonCertificatesProvider
+import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.CovidTestCertificatePendingCard
+import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.PersonCertificateCard
+import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository
+import de.rki.coronawarnapp.covidcertificate.valueset.ValueSetsRepository
+import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QrCodeGenerator
+import de.rki.coronawarnapp.ui.presencetracing.attendee.checkins.permission.CameraPermissionProvider
+import io.kotest.matchers.shouldBe
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.just
+import io.mockk.mockk
+import io.mockk.mockkStatic
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flowOf
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import testhelpers.BaseTest
+import testhelpers.TestDispatcherProvider
+import testhelpers.extensions.InstantExecutorExtension
+import testhelpers.extensions.getOrAwaitValue
+import java.util.Locale
+
+@ExtendWith(InstantExecutorExtension::class)
+class PersonOverviewViewModelTest : BaseTest() {
+
+    @MockK lateinit var qrCodeGenerator: QrCodeGenerator
+    @MockK lateinit var personCertificatesProvider: PersonCertificatesProvider
+    @MockK lateinit var testCertificateRepository: TestCertificateRepository
+    @MockK lateinit var refreshResult: TestCertificateRepository.RefreshResult
+    @MockK lateinit var valueSetsRepository: ValueSetsRepository
+    @MockK lateinit var context: Context
+    @MockK lateinit var cameraPermissionProvider: CameraPermissionProvider
+
+    @BeforeEach
+    fun setup() {
+        MockKAnnotations.init(this, true)
+        mockkStatic("de.rki.coronawarnapp.contactdiary.util.ContactDiaryExtensionsKt")
+        coEvery { testCertificateRepository.refresh(any()) } returns setOf(refreshResult)
+        coEvery { qrCodeGenerator.createQrCode(any(), any(), any(), any(), any()) } returns mockk()
+        every { personCertificatesProvider.personCertificates } returns emptyFlow()
+        every { refreshResult.error } returns null
+        every { testCertificateRepository.certificates } returns emptyFlow()
+        every { context.getLocale() } returns Locale.GERMAN
+        every { valueSetsRepository.triggerUpdateValueSet(any()) } just Runs
+        every { cameraPermissionProvider.deniedPermanently } returns flowOf(false)
+    }
+
+    @Test
+    fun `refreshCertificate causes an error dialog event`() {
+        val error = mockk<Exception>()
+        every { refreshResult.error } returns error
+
+        instance.apply {
+            refreshCertificate("Identifier")
+            events.getOrAwaitValue() shouldBe ShowRefreshErrorDialog(error)
+        }
+    }
+
+    @Test
+    fun `refreshCertificate triggers refresh operation in repo`() {
+        instance.refreshCertificate("Identifier")
+        coVerify { testCertificateRepository.refresh(any()) }
+    }
+
+    @Test
+    fun `deleteTestCertificate deletes certificates from repo`() {
+        coEvery { testCertificateRepository.deleteCertificate(any()) } just Runs
+        instance.apply {
+            deleteTestCertificate("Identifier")
+        }
+
+        coEvery { testCertificateRepository.deleteCertificate(any()) }
+    }
+
+    @Test
+    fun onScanQrCode() {
+        instance.apply {
+            onScanQrCode()
+            events.getOrAwaitValue() shouldBe ScanQrCode
+        }
+    }
+
+    @Test
+    fun `Sorting - List has pending certificate`() {
+        every { personCertificatesProvider.personCertificates } returns
+            flowOf(PersonCertificatesData.certificatesWithPending)
+        instance.personCertificates.getOrAwaitValue().apply {
+            (get(0) as CovidTestCertificatePendingCard.Item).apply { certificate.fullName shouldBe "Max Mustermann" }
+            (get(1) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Zeebee" }
+            (get(2) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Andrea Schneider" }
+        }
+    }
+
+    @Test
+    fun `Sorting - List has pending & updating certificate`() {
+        every { personCertificatesProvider.personCertificates } returns
+            flowOf(PersonCertificatesData.certificatesWithUpdating)
+        instance.personCertificates.getOrAwaitValue().apply {
+            (get(0) as CovidTestCertificatePendingCard.Item).apply { certificate.fullName shouldBe "Max Mustermann" }
+            (get(1) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Zeebee" }
+            (get(2) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Andrea Schneider" }
+        }
+    }
+
+    @Test
+    fun `Sorting - List has no CWA user`() {
+        every { personCertificatesProvider.personCertificates } returns
+            flowOf(PersonCertificatesData.certificatesWithoutCwaUser)
+        instance.personCertificates.getOrAwaitValue().apply {
+            (get(0) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Andrea Schneider" }
+            (get(1) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Erika Musterfrau" }
+            (get(2) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Max Mustermann" }
+        }
+    }
+
+    @Test
+    fun `Sorting - List has CWA user`() {
+        every { personCertificatesProvider.personCertificates } returns
+            flowOf(PersonCertificatesData.certificatesWithCwaUser)
+        instance.personCertificates.getOrAwaitValue().apply {
+            (get(0) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Zeebee" } // CWA user
+            (get(1) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Andrea Schneider" }
+            (get(2) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Erika Musterfrau" }
+            (get(3) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Max Mustermann" }
+            (get(4) as PersonCertificateCard.Item).apply { certificate.fullName shouldBe "Zeebee A" }
+        }
+    }
+
+    private val instance
+        get() = PersonOverviewViewModel(
+            dispatcherProvider = TestDispatcherProvider(),
+            testCertificateRepository = testCertificateRepository,
+            certificatesProvider = personCertificatesProvider,
+            qrCodeGenerator = qrCodeGenerator,
+            valueSetsRepository = valueSetsRepository,
+            context = context,
+            cameraPermissionProvider = cameraPermissionProvider
+        )
+}