Skip to content

Commit 86f4357

Browse files
committed
tests: much faster notification module tests
Mocked out timeout for external lifetime event callbacks, this made the tests for notifications take an extra 90 seconds. Also removed Robolectric from this test and replaced it with lighter weight mockk mocks.
1 parent 06f3246 commit 86f4357

File tree

3 files changed

+45
-40
lines changed

3 files changed

+45
-40
lines changed

OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/generation/impl/NotificationGenerationProcessor.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ internal class NotificationGenerationProcessor(
3737
private val _lifecycleService: INotificationLifecycleService,
3838
private val _time: ITime,
3939
) : INotificationGenerationProcessor {
40+
41+
private val EXTERNAL_CALLBACKS_TIMEOUT get() = 30_000L
42+
4043
override suspend fun processNotificationData(
4144
context: Context,
4245
androidNotificationId: Int,
@@ -67,7 +70,7 @@ internal class NotificationGenerationProcessor(
6770

6871
try {
6972
val notificationReceivedEvent = NotificationReceivedEvent(context, notification)
70-
withTimeout(30000L) {
73+
withTimeout(EXTERNAL_CALLBACKS_TIMEOUT) {
7174
launchOnIO {
7275
_lifecycleService.externalRemoteNotificationReceived(notificationReceivedEvent)
7376

@@ -100,7 +103,7 @@ internal class NotificationGenerationProcessor(
100103

101104
try {
102105
val notificationWillDisplayEvent = NotificationWillDisplayEvent(notificationJob.notification)
103-
withTimeout(30000L) {
106+
withTimeout(EXTERNAL_CALLBACKS_TIMEOUT) {
104107
launchOnIO {
105108
_lifecycleService.externalNotificationWillShowInForeground(notificationWillDisplayEvent)
106109

OneSignalSDK/onesignal/notifications/src/test/java/com/onesignal/notifications/internal/generation/NotificationGenerationProcessorTests.kt

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package com.onesignal.notifications.internal.generation
22

33
import android.content.Context
4-
import androidx.test.core.app.ApplicationProvider
5-
import br.com.colman.kotest.android.extensions.robolectric.RobolectricTest
64
import com.onesignal.common.threading.suspendifyOnIO
75
import com.onesignal.debug.LogLevel
86
import com.onesignal.debug.internal.logging.Logging
@@ -21,16 +19,19 @@ import io.mockk.coVerify
2119
import io.mockk.every
2220
import io.mockk.just
2321
import io.mockk.mockk
22+
import io.mockk.mockkStatic
2423
import io.mockk.runs
24+
import io.mockk.spyk
2525
import kotlinx.coroutines.delay
2626
import kotlinx.coroutines.withTimeout
2727
import org.json.JSONObject
28-
import org.robolectric.annotation.Config
2928

3029
// Mocks used by every test in this file
3130
private class Mocks {
3231
val notificationDisplayer = mockk<INotificationDisplayer>()
3332

33+
val context = mockk<Context>(relaxed = true)
34+
3435
val applicationService =
3536
run {
3637
val mockApplicationService = AndroidMockHelper.applicationService()
@@ -67,16 +68,21 @@ private class Mocks {
6768
mockNotificationRepository
6869
}
6970

70-
val notificationGenerationProcessor =
71-
NotificationGenerationProcessor(
72-
applicationService,
73-
notificationDisplayer,
74-
MockHelper.configModelStore(),
75-
notificationRepository,
76-
mockk(),
77-
notificationLifecycleService,
78-
MockHelper.time(1111),
71+
val notificationGenerationProcessor = run {
72+
val mock = spyk(
73+
NotificationGenerationProcessor(
74+
applicationService,
75+
notificationDisplayer,
76+
MockHelper.configModelStore(),
77+
notificationRepository,
78+
mockk(),
79+
notificationLifecycleService,
80+
MockHelper.time(1111),
81+
), recordPrivateCalls = true
7982
)
83+
every { mock getProperty "EXTERNAL_CALLBACKS_TIMEOUT" } answers { 10L }
84+
mock
85+
}
8086

8187
val notificationPayload: JSONObject =
8288
JSONObject()
@@ -89,26 +95,23 @@ private class Mocks {
8995
)
9096
}
9197

92-
@Config(
93-
packageName = "com.onesignal.example",
94-
sdk = [26],
95-
)
96-
@RobolectricTest
9798
class NotificationGenerationProcessorTests : FunSpec({
9899
beforeAny {
99100
Logging.logLevel = LogLevel.NONE
101+
102+
mockkStatic(android.text.TextUtils::class)
103+
every { android.text.TextUtils.isEmpty(any()) } answers { firstArg<CharSequence?>()?.isEmpty() ?: true }
100104
}
101105

102106
test("processNotificationData should set title correctly") {
103107
// Given
104-
val context = ApplicationProvider.getApplicationContext<Context>()
105108
val mocks = Mocks()
106109
coEvery { mocks.notificationDisplayer.displayNotification(any()) } returns true
107110
coEvery { mocks.notificationLifecycleService.externalRemoteNotificationReceived(any()) } just runs
108111
coEvery { mocks.notificationLifecycleService.externalNotificationWillShowInForeground(any()) } just runs
109112

110113
// When
111-
mocks.notificationGenerationProcessor.processNotificationData(context, 1, mocks.notificationPayload, false, 1111)
114+
mocks.notificationGenerationProcessor.processNotificationData(mocks.context, 1, mocks.notificationPayload, false, 1111)
112115

113116
// Then
114117
coVerify(exactly = 1) {
@@ -130,14 +133,13 @@ class NotificationGenerationProcessorTests : FunSpec({
130133

131134
test("processNotificationData should restore notification correctly") {
132135
// Given
133-
val context = ApplicationProvider.getApplicationContext<Context>()
134136
val mocks = Mocks()
135137
coEvery { mocks.notificationDisplayer.displayNotification(any()) } returns true
136138
coEvery { mocks.notificationLifecycleService.externalRemoteNotificationReceived(any()) } just runs
137139
coEvery { mocks.notificationLifecycleService.externalNotificationWillShowInForeground(any()) } just runs
138140

139141
// When
140-
mocks.notificationGenerationProcessor.processNotificationData(context, 1, mocks.notificationPayload, true, 1111)
142+
mocks.notificationGenerationProcessor.processNotificationData(mocks.context, 1, mocks.notificationPayload, true, 1111)
141143

142144
// Then
143145
coVerify(exactly = 1) {
@@ -156,30 +158,28 @@ class NotificationGenerationProcessorTests : FunSpec({
156158

157159
test("processNotificationData should not display notification when external callback indicates not to") {
158160
// Given
159-
val context = ApplicationProvider.getApplicationContext<Context>()
160161
val mocks = Mocks()
161162
coEvery { mocks.notificationLifecycleService.externalRemoteNotificationReceived(any()) } answers {
162163
val receivedEvent = firstArg<INotificationReceivedEvent>()
163164
receivedEvent.preventDefault()
164165
}
165166

166167
// When
167-
mocks.notificationGenerationProcessor.processNotificationData(context, 1, mocks.notificationPayload, false, 1111)
168+
mocks.notificationGenerationProcessor.processNotificationData(mocks.context, 1, mocks.notificationPayload, false, 1111)
168169

169170
// Then
170171
}
171172

172173
test("processNotificationData should display notification when external callback takes longer than 30 seconds") {
173174
// Given
174-
val context = ApplicationProvider.getApplicationContext<Context>()
175175
val mocks = Mocks()
176176
coEvery { mocks.notificationDisplayer.displayNotification(any()) } returns true
177177
coEvery { mocks.notificationLifecycleService.externalRemoteNotificationReceived(any()) } coAnswers {
178178
delay(40000)
179179
}
180180

181181
// When
182-
mocks.notificationGenerationProcessor.processNotificationData(context, 1, mocks.notificationPayload, true, 1111)
182+
mocks.notificationGenerationProcessor.processNotificationData(mocks.context, 1, mocks.notificationPayload, true, 1111)
183183

184184
// Then
185185
coVerify(exactly = 1) {
@@ -198,7 +198,6 @@ class NotificationGenerationProcessorTests : FunSpec({
198198

199199
test("processNotificationData should not display notification when foreground callback indicates not to") {
200200
// Given
201-
val context = ApplicationProvider.getApplicationContext<Context>()
202201
val mocks = Mocks()
203202
coEvery { mocks.notificationLifecycleService.externalRemoteNotificationReceived(any()) } just runs
204203
coEvery { mocks.notificationLifecycleService.externalNotificationWillShowInForeground(any()) } answers {
@@ -207,22 +206,21 @@ class NotificationGenerationProcessorTests : FunSpec({
207206
}
208207

209208
// When
210-
mocks.notificationGenerationProcessor.processNotificationData(context, 1, mocks.notificationPayload, false, 1111)
209+
mocks.notificationGenerationProcessor.processNotificationData(mocks.context, 1, mocks.notificationPayload, false, 1111)
211210

212211
// Then
213212
}
214213

215214
test("processNotificationData should display notification when foreground callback takes longer than 30 seconds") {
216215
// Given
217-
val context = ApplicationProvider.getApplicationContext<Context>()
218216
val mocks = Mocks()
219217
coEvery { mocks.notificationDisplayer.displayNotification(any()) } returns true
220218
coEvery { mocks.notificationLifecycleService.externalNotificationWillShowInForeground(any()) } coAnswers {
221219
delay(40000)
222220
}
223221

224222
// When
225-
mocks.notificationGenerationProcessor.processNotificationData(context, 1, mocks.notificationPayload, true, 1111)
223+
mocks.notificationGenerationProcessor.processNotificationData(mocks.context, 1, mocks.notificationPayload, true, 1111)
226224

227225
// Then
228226
coVerify(exactly = 1) {
@@ -241,7 +239,6 @@ class NotificationGenerationProcessorTests : FunSpec({
241239

242240
test("processNotificationData should immediately drop the notification when will display callback indicates to") {
243241
// Given
244-
val context = ApplicationProvider.getApplicationContext<Context>()
245242
val mocks = Mocks()
246243
coEvery { mocks.notificationLifecycleService.externalRemoteNotificationReceived(any()) } just runs
247244
coEvery { mocks.notificationLifecycleService.externalNotificationWillShowInForeground(any()) } answers {
@@ -252,13 +249,12 @@ class NotificationGenerationProcessorTests : FunSpec({
252249

253250
// If discard is set to false this should timeout waiting for display()
254251
withTimeout(1_000) {
255-
mocks.notificationGenerationProcessor.processNotificationData(context, 1, mocks.notificationPayload, false, 1111)
252+
mocks.notificationGenerationProcessor.processNotificationData(mocks.context, 1, mocks.notificationPayload, false, 1111)
256253
}
257254
}
258255

259256
test("processNotificationData should immediately drop the notification when received event callback indicates to") {
260257
// Given
261-
val context = ApplicationProvider.getApplicationContext<Context>()
262258
val mocks = Mocks()
263259
coEvery { mocks.notificationLifecycleService.externalRemoteNotificationReceived(any()) } answers {
264260
val receivedEvent = firstArg<INotificationReceivedEvent>()
@@ -268,13 +264,12 @@ class NotificationGenerationProcessorTests : FunSpec({
268264

269265
// If discard is set to false this should timeout waiting for display()
270266
withTimeout(1_000) {
271-
mocks.notificationGenerationProcessor.processNotificationData(context, 1, mocks.notificationPayload, false, 1111)
267+
mocks.notificationGenerationProcessor.processNotificationData(mocks.context, 1, mocks.notificationPayload, false, 1111)
272268
}
273269
}
274270

275271
test("processNotificationData allows the will display callback to prevent default behavior twice") {
276272
// Given
277-
val context = ApplicationProvider.getApplicationContext<Context>()
278273
val mocks = Mocks()
279274
coEvery { mocks.notificationDisplayer.displayNotification(any()) } returns true
280275
coEvery { mocks.notificationLifecycleService.externalRemoteNotificationReceived(any()) } just runs
@@ -290,7 +285,7 @@ class NotificationGenerationProcessorTests : FunSpec({
290285
}
291286

292287
// When
293-
mocks.notificationGenerationProcessor.processNotificationData(context, 1, mocks.notificationPayload, false, 1111)
288+
mocks.notificationGenerationProcessor.processNotificationData(mocks.context, 1, mocks.notificationPayload, false, 1111)
294289

295290
// Then
296291
coVerify(exactly = 0) {
@@ -300,7 +295,6 @@ class NotificationGenerationProcessorTests : FunSpec({
300295

301296
test("processNotificationData allows the received event callback to prevent default behavior twice") {
302297
// Given
303-
val context = ApplicationProvider.getApplicationContext<Context>()
304298
val mocks = Mocks()
305299
coEvery { mocks.notificationDisplayer.displayNotification(any()) } returns true
306300
coEvery { mocks.notificationLifecycleService.externalRemoteNotificationReceived(any()) } coAnswers {
@@ -315,7 +309,7 @@ class NotificationGenerationProcessorTests : FunSpec({
315309
}
316310

317311
// When
318-
mocks.notificationGenerationProcessor.processNotificationData(context, 1, mocks.notificationPayload, true, 1111)
312+
mocks.notificationGenerationProcessor.processNotificationData(mocks.context, 1, mocks.notificationPayload, true, 1111)
319313

320314
// Then
321315
coVerify(exactly = 0) {

OneSignalSDK/onesignal/testhelpers/src/main/java/com/onesignal/mocks/AndroidMockHelper.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.onesignal.mocks
22

3+
import android.content.Context
34
import androidx.test.core.app.ApplicationProvider
45
import com.onesignal.core.internal.application.IApplicationService
56
import io.mockk.every
7+
import io.mockk.mockk
68

79
/**
810
* Singleton which provides common mock services when running in an Android environment.
@@ -11,7 +13,13 @@ object AndroidMockHelper {
1113
fun applicationService(): IApplicationService {
1214
val mockAppService = MockHelper.applicationService()
1315

14-
every { mockAppService.appContext } returns ApplicationProvider.getApplicationContext()
16+
try {
17+
// Robolectric
18+
every { mockAppService.appContext } returns ApplicationProvider.getApplicationContext()
19+
} catch (_: IllegalStateException) {
20+
// Fallback to simpler mock (using mockk) if Robolectric is not used in the test
21+
every { mockAppService.appContext } returns mockk<Context>(relaxed = true)
22+
}
1523

1624
return mockAppService
1725
}

0 commit comments

Comments
 (0)