mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-27 03:49:04 +03:00
adding unit tests around the analytics impl
This commit is contained in:
parent
837caabcec
commit
e36e67c54c
6 changed files with 342 additions and 0 deletions
|
@ -113,6 +113,7 @@ class DefaultVectorAnalytics @Inject constructor(
|
|||
private fun observeUserConsent() {
|
||||
getUserConsent()
|
||||
.onEach { consent ->
|
||||
println("!!!, got consent: $consent")
|
||||
Timber.tag(analyticsTag.value).d("User consent updated to $consent")
|
||||
userConsent = consent
|
||||
optOutPostHog()
|
||||
|
@ -147,6 +148,9 @@ class DefaultVectorAnalytics @Inject constructor(
|
|||
|
||||
override fun screen(screen: VectorAnalyticsScreen) {
|
||||
Timber.tag(analyticsTag.value).d("screen($screen)")
|
||||
|
||||
println("userconsnet: $userConsent")
|
||||
|
||||
posthog
|
||||
?.takeIf { userConsent == true }
|
||||
?.screen(screen.getName(), screen.getProperties()?.toPostHogProperties())
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.analytics.impl
|
||||
|
||||
import com.posthog.android.Properties
|
||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
||||
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
|
||||
import im.vector.app.test.fakes.FakeAnalyticsStore
|
||||
import im.vector.app.test.fakes.FakePostHog
|
||||
import im.vector.app.test.fakes.FakePostHogFactory
|
||||
import im.vector.app.test.fixtures.AnalyticsConfigFixture.anAnalyticsConfig
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
private const val AN_ANALYTICS_ID = "analytics-id"
|
||||
private val A_SCREEN_EVENT = object : VectorAnalyticsScreen {
|
||||
override fun getName() = "a-screen-event-name"
|
||||
override fun getProperties() = mapOf("property-name" to "property-value")
|
||||
}
|
||||
private val AN_EVENT = object : VectorAnalyticsEvent {
|
||||
override fun getName() = "an-event-name"
|
||||
override fun getProperties() = mapOf("property-name" to "property-value")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class DefaultVectorAnalyticsTest {
|
||||
|
||||
private val fakePostHog = FakePostHog()
|
||||
private val fakeAnalyticsStore = FakeAnalyticsStore()
|
||||
|
||||
private val defaultVectorAnalytics = DefaultVectorAnalytics(
|
||||
postHogFactory = FakePostHogFactory(fakePostHog.instance).instance,
|
||||
analyticsStore = fakeAnalyticsStore.instance,
|
||||
globalScope = CoroutineScope(Dispatchers.Unconfined),
|
||||
analyticsConfig = anAnalyticsConfig(isEnabled = true)
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
defaultVectorAnalytics.init()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when setting user consent then updates analytics store`() = runBlockingTest {
|
||||
defaultVectorAnalytics.setUserConsent(true)
|
||||
|
||||
fakeAnalyticsStore.verifyConsentUpdated(updatedValue = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when consenting to analytics then updates posthog opt out to false`() = runBlockingTest {
|
||||
fakeAnalyticsStore.givenUserContent(consent = true)
|
||||
|
||||
fakePostHog.verifyOptOutStatus(optedOut = false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when revoking consent to analytics then updates posthog opt out to true`() = runBlockingTest {
|
||||
fakeAnalyticsStore.givenUserContent(consent = false)
|
||||
|
||||
fakePostHog.verifyOptOutStatus(optedOut = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when setting the analytics id then updates analytics store`() = runBlockingTest {
|
||||
defaultVectorAnalytics.setAnalyticsId(AN_ANALYTICS_ID)
|
||||
|
||||
fakeAnalyticsStore.verifyAnalyticsIdUpdated(updatedValue = AN_ANALYTICS_ID)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when valid analytics id updates then identify`() = runBlockingTest {
|
||||
fakeAnalyticsStore.givenAnalyticsId(AN_ANALYTICS_ID)
|
||||
|
||||
fakePostHog.verifyIdentifies(AN_ANALYTICS_ID)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when signing out analytics id updates then resets`() = runBlockingTest {
|
||||
fakeAnalyticsStore.allowSettingAnalyticsIdToCallBackingFlow()
|
||||
|
||||
defaultVectorAnalytics.onSignOut()
|
||||
|
||||
fakePostHog.verifyReset()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given user consent when tracking screen events then submits to posthog`() = runBlockingTest {
|
||||
fakeAnalyticsStore.givenUserContent(consent = true)
|
||||
|
||||
defaultVectorAnalytics.screen(A_SCREEN_EVENT)
|
||||
|
||||
fakePostHog.verifyScreenTracked(A_SCREEN_EVENT.getName(), Properties().also {
|
||||
it.putAll(A_SCREEN_EVENT.getProperties())
|
||||
})
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given user has not consented when tracking screen events then does not track`() = runBlockingTest {
|
||||
fakeAnalyticsStore.givenUserContent(consent = false)
|
||||
|
||||
defaultVectorAnalytics.screen(A_SCREEN_EVENT)
|
||||
|
||||
fakePostHog.verifyNoScreenTracking()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given user consent when tracking events then submits to posthog`() = runBlockingTest {
|
||||
fakeAnalyticsStore.givenUserContent(consent = true)
|
||||
|
||||
defaultVectorAnalytics.capture(AN_EVENT)
|
||||
|
||||
fakePostHog.verifyEventTracked(AN_EVENT.getName(), Properties().also {
|
||||
it.putAll(AN_EVENT.getProperties())
|
||||
})
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given user has not consented when tracking events then does not track`() = runBlockingTest {
|
||||
fakeAnalyticsStore.givenUserContent(consent = false)
|
||||
|
||||
defaultVectorAnalytics.capture(AN_EVENT)
|
||||
|
||||
fakePostHog.verifyNoEventTracking()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.test.fakes
|
||||
|
||||
import im.vector.app.features.analytics.store.AnalyticsStore
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
class FakeAnalyticsStore {
|
||||
|
||||
private val _consentFlow = MutableSharedFlow<Boolean>()
|
||||
private val _idFlow = MutableSharedFlow<String>()
|
||||
|
||||
val instance = mockk<AnalyticsStore>(relaxed = true) {
|
||||
every { userConsentFlow } returns _consentFlow
|
||||
every { analyticsIdFlow } returns _idFlow
|
||||
}
|
||||
|
||||
fun allowSettingAnalyticsIdToCallBackingFlow() {
|
||||
coEvery { instance.setAnalyticsId(any()) } answers {
|
||||
runBlocking { _idFlow.emit(firstArg()) }
|
||||
}
|
||||
}
|
||||
|
||||
fun verifyConsentUpdated(updatedValue: Boolean) {
|
||||
coVerify { instance.setUserConsent(updatedValue) }
|
||||
}
|
||||
|
||||
suspend fun givenUserContent(consent: Boolean) {
|
||||
_consentFlow.emit(consent)
|
||||
}
|
||||
|
||||
fun verifyAnalyticsIdUpdated(updatedValue: String) {
|
||||
coVerify { instance.setAnalyticsId(updatedValue) }
|
||||
}
|
||||
|
||||
suspend fun givenAnalyticsId(id: String) {
|
||||
_idFlow.emit(id)
|
||||
}
|
||||
}
|
75
vector/src/test/java/im/vector/app/test/fakes/FakePostHog.kt
Normal file
75
vector/src/test/java/im/vector/app/test/fakes/FakePostHog.kt
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.test.fakes
|
||||
|
||||
import android.os.Looper
|
||||
import com.posthog.android.PostHog
|
||||
import com.posthog.android.Properties
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.verify
|
||||
|
||||
class FakePostHog {
|
||||
|
||||
init {
|
||||
// workaround to avoid PostHog.HANDLER failing
|
||||
mockkStatic(Looper::class)
|
||||
val looper = mockk<Looper> {
|
||||
every { thread } returns Thread.currentThread()
|
||||
}
|
||||
every { Looper.getMainLooper() } returns looper
|
||||
}
|
||||
|
||||
val instance = mockk<PostHog>(relaxed = true)
|
||||
|
||||
fun verifyOptOutStatus(optedOut: Boolean) {
|
||||
verify { instance.optOut(optedOut) }
|
||||
}
|
||||
|
||||
fun verifyIdentifies(analyticsId: String) {
|
||||
verify { instance.identify(analyticsId) }
|
||||
}
|
||||
|
||||
fun verifyReset() {
|
||||
verify { instance.reset() }
|
||||
}
|
||||
|
||||
fun verifyScreenTracked(name: String, properties: Properties) {
|
||||
verify { instance.screen(name, properties) }
|
||||
}
|
||||
|
||||
fun verifyNoScreenTracking() {
|
||||
verify(exactly = 0) {
|
||||
instance.screen(any())
|
||||
instance.screen(any(), any())
|
||||
instance.screen(any(), any(), any())
|
||||
}
|
||||
}
|
||||
|
||||
fun verifyEventTracked(name: String, properties: Properties) {
|
||||
verify { instance.capture(name, properties) }
|
||||
}
|
||||
|
||||
fun verifyNoEventTracking() {
|
||||
verify(exactly = 0) {
|
||||
instance.capture(any())
|
||||
instance.capture(any(), any())
|
||||
instance.capture(any(), any(), any())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.test.fakes
|
||||
|
||||
import com.posthog.android.PostHog
|
||||
import im.vector.app.features.analytics.impl.PostHogFactory
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
|
||||
class FakePostHogFactory(postHog: PostHog) {
|
||||
val instance = mockk<PostHogFactory>().also {
|
||||
every { it.createPosthog() } returns postHog
|
||||
}
|
||||
}
|
33
vector/src/test/java/im/vector/app/test/fixtures/AnalyticsConfigFixture.kt
vendored
Normal file
33
vector/src/test/java/im/vector/app/test/fixtures/AnalyticsConfigFixture.kt
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.test.fixtures
|
||||
|
||||
import im.vector.app.features.analytics.AnalyticsConfig
|
||||
|
||||
object AnalyticsConfigFixture {
|
||||
fun anAnalyticsConfig(
|
||||
isEnabled: Boolean = false,
|
||||
postHogHost: String = "http://posthog.url",
|
||||
postHogApiKey: String = "api-key",
|
||||
policyLink: String = "http://policy.link"
|
||||
) = object : AnalyticsConfig {
|
||||
override val isEnabled: Boolean = isEnabled
|
||||
override val postHogHost = postHogHost
|
||||
override val postHogApiKey = postHogApiKey
|
||||
override val policyLink = policyLink
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue