Skip to content

Commit 563f084

Browse files
refactor UI tests to use coroutine-based testing for improved async handling
1 parent 0ac9ff1 commit 563f084

14 files changed

Lines changed: 417 additions & 398 deletions
Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package presentation.audit
22

33
import io.mockk.*
4+
import kotlinx.coroutines.test.runTest
45
import org.junit.jupiter.api.BeforeEach
56
import org.junit.jupiter.api.Test
67
import presentation.io.ConsoleIO
@@ -24,96 +25,96 @@ class AuditUITest {
2425
}
2526

2627
@Test
27-
fun `should call viewAuditLogsByProjectUI when user selects option 1`() {
28+
fun `should call viewAuditLogsByProjectUI when user selects option 1`() = runTest {
2829
// Given
29-
every { consoleIO.write(any()) } just runs
30-
every { consoleIO.read() } returns "1" andThen "3"
31-
every { viewAuditLogsByProjectUI.invoke() } just runs
30+
coEvery { consoleIO.write(any()) } just runs
31+
coEvery { consoleIO.read() } returns "1" andThen "3"
32+
coEvery { viewAuditLogsByProjectUI.invoke() } just runs
3233

3334
// When
3435
auditUI()
3536

3637
// Then
37-
verify(exactly = 2) { consoleIO.write(any()) }
38-
verify(exactly = 2) { consoleIO.read() }
39-
verify(exactly = 1) { viewAuditLogsByProjectUI.invoke() }
38+
coVerify(exactly = 2) { consoleIO.write(any()) }
39+
coVerify(exactly = 2) { consoleIO.read() }
40+
coVerify(exactly = 1) { viewAuditLogsByProjectUI.invoke() }
4041
}
4142

4243
@Test
43-
fun `should call viewAuditLogsByTaskUI when user selects option 2`() {
44+
fun `should call viewAuditLogsByTaskUI when user selects option 2`() = runTest {
4445
// Given
45-
every { consoleIO.write(any()) } just runs
46-
every { consoleIO.read() } returns "2" andThen "3"
47-
every { viewAuditLogsByTaskUI.invoke() } just runs
46+
coEvery { consoleIO.write(any()) } just runs
47+
coEvery { consoleIO.read() } returns "2" andThen "3"
48+
coEvery { viewAuditLogsByTaskUI.invoke() } just runs
4849

4950
// When
5051
auditUI()
5152

5253
// Then
53-
verify(exactly = 2) { consoleIO.write(any()) }
54-
verify(exactly = 2) { consoleIO.read() }
55-
verify(exactly = 1) { viewAuditLogsByTaskUI.invoke() }
54+
coVerify(exactly = 2) { consoleIO.write(any()) }
55+
coVerify(exactly = 2) { consoleIO.read() }
56+
coVerify(exactly = 1) { viewAuditLogsByTaskUI.invoke() }
5657
}
5758

5859
@Test
59-
fun `should exit when user selects option 3`() {
60+
fun `should exit when user selects option 3`() = runTest {
6061
// Given
61-
every { consoleIO.write(any()) } just runs
62-
every { consoleIO.read() } returns "3"
62+
coEvery { consoleIO.write(any()) } just runs
63+
coEvery { consoleIO.read() } returns "3"
6364

6465
// When
6566
auditUI()
6667

6768
// Then
68-
verify(exactly = 1) { consoleIO.write(any()) }
69-
verify(exactly = 1) { consoleIO.read() }
69+
coVerify(exactly = 1) { consoleIO.write(any()) }
70+
coVerify(exactly = 1) { consoleIO.read() }
7071
}
7172

7273
@Test
73-
fun `should show error message and continue when user enters invalid option`() {
74+
fun `should show error message and continue when user enters invalid option`() = runTest {
7475
// Given
75-
every { consoleIO.write(any()) } just runs
76-
every { consoleIO.read() } returns "invalid" andThen "3"
76+
coEvery { consoleIO.write(any()) } just runs
77+
coEvery { consoleIO.read() } returns "invalid" andThen "3"
7778

7879
// When
7980
auditUI()
8081

8182
// Then
82-
verify(exactly = 3) { consoleIO.write(any()) } // Menu + Error message + Menu again
83-
verify(exactly = 2) { consoleIO.read() }
83+
coVerify(exactly = 3) { consoleIO.write(any()) } // Menu + Error message + Menu again
84+
coVerify(exactly = 2) { consoleIO.read() }
8485
}
8586

8687
@Test
87-
fun `should show error message and continue when user enters out of range number`() {
88+
fun `should show error message and continue when user enters out of range number`() = runTest {
8889
// Given
89-
every { consoleIO.write(any()) } just runs
90-
every { consoleIO.read() } returns "5" andThen "3"
90+
coEvery { consoleIO.write(any()) } just runs
91+
coEvery { consoleIO.read() } returns "5" andThen "3"
9192

9293
// When
9394
auditUI()
9495

9596
// Then
96-
verify(exactly = 3) { consoleIO.write(any()) } // Menu + Error message + Menu again
97-
verify(exactly = 2) { consoleIO.read() }
97+
coVerify(exactly = 3) { consoleIO.write(any()) } // Menu + Error message + Menu again
98+
coVerify(exactly = 2) { consoleIO.read() }
9899
}
99100

100101
@Test
101-
fun `should handle multiple valid inputs before exiting`() {
102+
fun `should handle multiple valid inputs before exiting`() = runTest {
102103
// Given
103-
every { consoleIO.write(any()) } just runs
104+
coEvery { consoleIO.write(any()) } just runs
104105

105106
val inputs = mutableListOf("1", "2", "3")
106-
every { consoleIO.read() } answers { inputs.removeAt(0) }
107+
coEvery { consoleIO.read() } answers { inputs.removeAt(0) }
107108

108-
every { viewAuditLogsByProjectUI.invoke() } just runs
109-
every { viewAuditLogsByTaskUI.invoke() } just runs
109+
coEvery { viewAuditLogsByProjectUI.invoke() } just runs
110+
coEvery { viewAuditLogsByTaskUI.invoke() } just runs
110111

111112
// When
112113
auditUI()
113114

114115
// Then
115-
verify { viewAuditLogsByProjectUI.invoke() }
116-
verify { viewAuditLogsByTaskUI.invoke() }
117-
verify(exactly = 3) { consoleIO.read() }
116+
coVerify { viewAuditLogsByProjectUI.invoke() }
117+
coVerify { viewAuditLogsByTaskUI.invoke() }
118+
coVerify(exactly = 3) { consoleIO.read() }
118119
}
119-
}
120+
}

src/test/kotlin/presentation/audit/ViewAuditLogsByProjectUITest.kt

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ package presentation.audit
33
import domain.models.AuditLog
44
import domain.models.AuditLog.AuditType
55
import domain.usecases.audit.ViewAuditLogsByProjectUseCase
6-
import io.mockk.every
6+
import io.mockk.coEvery
7+
import io.mockk.coVerify
8+
import io.mockk.coVerifySequence
79
import io.mockk.mockk
8-
import io.mockk.verify
9-
import io.mockk.verifySequence
10+
import kotlinx.coroutines.test.runTest
1011
import kotlinx.datetime.Instant
1112
import kotlinx.datetime.TimeZone
1213
import kotlinx.datetime.toLocalDateTime
@@ -47,16 +48,16 @@ class ViewAuditLogsByProjectUITest {
4748
}
4849

4950
@Test
50-
fun `should display logs and return on exit command`() {
51+
fun `should display logs and return on exit command`() = runTest {
5152
// Given
52-
every { consoleIO.read() } returnsMany listOf(projectIdString, "exit")
53-
every { viewAuditLogsByProjectUseCase(projectId) } returns sampleAuditLogs
53+
coEvery { consoleIO.read() } returnsMany listOf(projectIdString, "exit")
54+
coEvery { viewAuditLogsByProjectUseCase(projectId) } returns sampleAuditLogs
5455

5556
// When
5657
viewAuditLogsByProjectUI.invoke()
5758

5859
// Then
59-
verifySequence {
60+
coVerifySequence {
6061
consoleIO.write("Enter project ID (or type 'exit' to quit): ")
6162
consoleIO.read()
6263
viewAuditLogsByProjectUseCase(projectId)
@@ -71,16 +72,16 @@ class ViewAuditLogsByProjectUITest {
7172
}
7273

7374
@Test
74-
fun `should handle empty logs`() {
75+
fun `should handle empty logs`() = runTest {
7576
// Given
76-
every { consoleIO.read() } returnsMany listOf(projectIdString, "exit")
77-
every { viewAuditLogsByProjectUseCase(projectId) } returns emptyList()
77+
coEvery { consoleIO.read() } returnsMany listOf(projectIdString, "exit")
78+
coEvery { viewAuditLogsByProjectUseCase(projectId) } returns emptyList()
7879

7980
// When
8081
viewAuditLogsByProjectUI.invoke()
8182

8283
// Then
83-
verifySequence {
84+
coVerifySequence {
8485
consoleIO.write("Enter project ID (or type 'exit' to quit): ")
8586
consoleIO.read()
8687
viewAuditLogsByProjectUseCase(projectId)
@@ -91,17 +92,17 @@ class ViewAuditLogsByProjectUITest {
9192
}
9293

9394
@Test
94-
fun `should handle exception when retrieving logs`() {
95+
fun `should handle exception when retrieving logs`() = runTest {
9596
// Given
9697
val errorMessage = "Database connection error"
97-
every { consoleIO.read() } returnsMany listOf(projectIdString, "exit")
98-
every { viewAuditLogsByProjectUseCase(projectId) } throws RuntimeException(errorMessage)
98+
coEvery { consoleIO.read() } returnsMany listOf(projectIdString, "exit")
99+
coEvery { viewAuditLogsByProjectUseCase(projectId) } throws RuntimeException(errorMessage)
99100

100101
// When
101102
viewAuditLogsByProjectUI.invoke()
102103

103104
// Then
104-
verifySequence {
105+
coVerifySequence {
105106
consoleIO.write("Enter project ID (or type 'exit' to quit): ")
106107
consoleIO.read()
107108
viewAuditLogsByProjectUseCase(projectId)
@@ -112,16 +113,16 @@ class ViewAuditLogsByProjectUITest {
112113
}
113114

114115
@Test
115-
fun `should handle invalid UUID input and retry`() {
116+
fun `should handle invalid UUID input and retry`() = runTest {
116117
// Given
117-
every { consoleIO.read() } returnsMany listOf("invalid-uuid", projectIdString, "exit")
118-
every { viewAuditLogsByProjectUseCase(projectId) } returns sampleAuditLogs
118+
coEvery { consoleIO.read() } returnsMany listOf("invalid-uuid", projectIdString, "exit")
119+
coEvery { viewAuditLogsByProjectUseCase(projectId) } returns sampleAuditLogs
119120

120121
// When
121122
viewAuditLogsByProjectUI.invoke()
122123

123124
// Then
124-
verifySequence {
125+
coVerifySequence {
125126
consoleIO.write("Enter project ID (or type 'exit' to quit): ")
126127
consoleIO.read()
127128
consoleIO.write(match { it.contains("❌ Invalid UUID format") })
@@ -139,42 +140,42 @@ class ViewAuditLogsByProjectUITest {
139140
}
140141

141142
@Test
142-
fun `should exit immediately when exit command is entered`() {
143+
fun `should exit immediately when exit command is entered`() = runTest {
143144
// Given
144-
every { consoleIO.read() } returns "exit"
145+
coEvery { consoleIO.read() } returns "exit"
145146

146147
// When
147148
viewAuditLogsByProjectUI.invoke()
148149

149150
// Then
150-
verifySequence {
151+
coVerifySequence {
151152
consoleIO.write("Enter project ID (or type 'exit' to quit): ")
152153
consoleIO.read()
153154
}
154-
verify(exactly = 0) {
155+
coVerify(exactly = 0) {
155156
viewAuditLogsByProjectUseCase(any())
156157
}
157158
}
158159

159160
@Test
160-
fun `should handle multiple valid queries before exiting`() {
161+
fun `should handle multiple valid queries before exiting`() = runTest {
161162
// Given
162163
val secondProjectId = UUID.randomUUID()
163-
every { consoleIO.read() } returnsMany listOf(
164+
coEvery { consoleIO.read() } returnsMany listOf(
164165
projectIdString,
165166
secondProjectId.toString(),
166167
"exit"
167168
)
168-
every { viewAuditLogsByProjectUseCase(projectId) } returns sampleAuditLogs
169-
every { viewAuditLogsByProjectUseCase(secondProjectId) } returns listOf(sampleAuditLogs.first())
169+
coEvery { viewAuditLogsByProjectUseCase(projectId) } returns sampleAuditLogs
170+
coEvery { viewAuditLogsByProjectUseCase(secondProjectId) } returns listOf(sampleAuditLogs.first())
170171

171172
// When
172173
viewAuditLogsByProjectUI.invoke()
173174

174175
// Then
175-
verify(exactly = 3) { consoleIO.write("Enter project ID (or type 'exit' to quit): ") }
176-
verify(exactly = 3) { consoleIO.read() }
177-
verify(exactly = 1) { viewAuditLogsByProjectUseCase(projectId) }
178-
verify(exactly = 1) { viewAuditLogsByProjectUseCase(secondProjectId) }
176+
coVerify(exactly = 3) { consoleIO.write("Enter project ID (or type 'exit' to quit): ") }
177+
coVerify(exactly = 3) { consoleIO.read() }
178+
coVerify(exactly = 1) { viewAuditLogsByProjectUseCase(projectId) }
179+
coVerify(exactly = 1) { viewAuditLogsByProjectUseCase(secondProjectId) }
179180
}
180-
}
181+
}

0 commit comments

Comments
 (0)