Skip to content

Commit 411907a

Browse files
authored
Merge pull request #2417 from OneSignal/tests/notification_test_speed
tests: Much faster notification module tests
2 parents 06f3246 + 86f4357 commit 411907a

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)